pixel-data-js 0.5.2 → 0.8.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.
@@ -1,45 +1,39 @@
1
- import type { BlendColor32, Color32 } from './_types'
1
+ import type { BlendColor32, Color32 } from '../_types'
2
+ import { BlendMode } from './blend-modes'
2
3
 
3
- export const overwriteColor32: BlendColor32 = (src, dst) => src
4
+ export const overwriteFast: BlendColor32 = (src, _dst) => src
4
5
 
5
- export const sourceOverColor32: BlendColor32 = (src, dst) => {
6
- const a = (src >>> 24) & 0xFF
7
- if (a === 255) return src
8
- if (a === 0) return dst
9
-
10
- // Pattern: (src * a + dst * (255 - a)) >> 8
11
- // We process RB and G separately so they don't overflow into each other
12
- const rbMask = 0xFF00FF
13
- const gMask = 0x00FF00
6
+ export const sourceOverFast: BlendColor32 = (src, dst) => {
7
+ const sa = (src >>> 24) & 0xFF
8
+ if (sa === 255) return src
9
+ if (sa === 0) return dst
14
10
 
15
- const sRB = src & rbMask
16
- const sG = src & gMask
17
- const dRB = dst & rbMask
18
- const dG = dst & gMask
11
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
12
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
19
13
 
20
- const invA = 255 - a
14
+ const da = (dst >>> 24) & 0xFF
21
15
 
22
- const outRB = ((sRB * a + dRB * invA) >> 8) & rbMask
23
- const outG = ((sG * a + dG * invA) >> 8) & gMask
16
+ // Alpha Lerp inlined
17
+ const invA = 255 - sa
18
+ const r = (sr * sa + dr * invA) >> 8
19
+ const g = (sg * sa + dg * invA) >> 8
20
+ const b = (sb * sa + db * invA) >> 8
21
+ const a = (255 * sa + da * invA) >> 8
24
22
 
25
- // Re-pack with opaque alpha (or calculate combined alpha if needed)
26
- const outA = (a + (((dst >>> 24) & 0xFF) * invA >> 8))
27
- return ((outA << 24) | outRB | outG) >>> 0 as Color32
23
+ return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
28
24
  }
29
25
 
30
- /** Math.min(src, dst) */
31
- export const darkenColor32: BlendColor32 = (src, dst) => {
26
+ export const darkenFast: BlendColor32 = (src, dst) => {
32
27
  const sa = (src >>> 24) & 0xFF
33
28
  if (sa === 0) return dst
34
- const br = Math.min(src & 0xFF, dst & 0xFF)
35
- const bg = Math.min((src >> 8) & 0xFF, (dst >> 8) & 0xFF)
36
- const bb = Math.min((src >> 16) & 0xFF, (dst >> 16) & 0xFF)
29
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
30
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
37
31
 
38
- if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
32
+ const br = sr < dr ? sr : dr
33
+ const bg = sg < dg ? sg : dg
34
+ const bb = sb < db ? sb : db
39
35
 
40
- const dr = dst & 0xFF
41
- const dg = (dst >> 8) & 0xFF
42
- const db = (dst >> 16) & 0xFF
36
+ if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
43
37
 
44
38
  // Alpha Lerp inlined
45
39
  const invA = 255 - sa
@@ -52,64 +46,71 @@ export const darkenColor32: BlendColor32 = (src, dst) => {
52
46
  }
53
47
 
54
48
  /** (src * dst) / 255 */
55
- export const multiplyColor32: BlendColor32 = (src, dst) => {
49
+ export const multiplyFast: BlendColor32 = (src, dst) => {
56
50
  const sa = (src >>> 24) & 0xFF
57
51
  if (sa === 0) return dst
52
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
53
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
58
54
 
59
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
60
-
61
- const br = ((src & 0xFF) * dr + 128) >> 8
62
- const bg = (((src >> 8) & 0xFF) * dg) >> 8
63
- const bb = (((src >> 16) & 0xFF) * db) >> 8
55
+ // Consistent floor rounding for all channels
56
+ const br = (sr * dr) >> 8
57
+ const bg = (sg * dg) >> 8
58
+ const bb = (sb * db) >> 8
64
59
 
65
60
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
66
61
 
67
62
  // Alpha Lerp inlined
68
63
  const invA = 255 - sa
64
+ const da = (dst >>> 24) & 0xFF
65
+
69
66
  const r = (br * sa + dr * invA) >> 8
70
67
  const g = (bg * sa + dg * invA) >> 8
71
68
  const b = (bb * sa + db * invA) >> 8
72
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8
69
+ const a = (255 * sa + da * invA) >> 8
73
70
 
74
71
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
75
72
  }
76
73
 
77
74
  /** 255 - (255-src)/dst */
78
- export const colorBurnColor32: BlendColor32 = (src, dst) => {
75
+ export const colorBurnFast: BlendColor32 = (src, dst) => {
79
76
  const sa = (src >>> 24) & 0xFF
80
77
  if (sa === 0) return dst
81
78
 
82
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
83
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
79
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
80
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
84
81
 
85
- const br = dr === 255 ? 255 : Math.max(0, 255 - ((255 - dr) << 8) / (sr || 1))
86
- const bg = dg === 255 ? 255 : Math.max(0, 255 - ((255 - dg) << 8) / (sg || 1))
87
- const bb = db === 255 ? 255 : Math.max(0, 255 - ((255 - db) << 8) / (sb || 1))
82
+ const br = dr === 255 ? 255 : sr === 0 ? 0 : Math.max(0, 255 - (((255 - dr) << 8) / sr) | 0)
83
+ const bg = dg === 255 ? 255 : sg === 0 ? 0 : Math.max(0, 255 - (((255 - dg) << 8) / sg) | 0)
84
+ const bb = db === 255 ? 255 : sb === 0 ? 0 : Math.max(0, 255 - (((255 - db) << 8) / sb) | 0)
88
85
 
89
86
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
90
87
 
91
- // Alpha Lerp inlined
92
88
  const invA = 255 - sa
89
+ const da = (dst >>> 24) & 0xFF
90
+
93
91
  const r = (br * sa + dr * invA) >> 8
94
92
  const g = (bg * sa + dg * invA) >> 8
95
93
  const b = (bb * sa + db * invA) >> 8
96
- const a = (255 * sa + ((dst >>> 24) & 0xFF) * invA) >> 8
94
+ const a = (255 * sa + da * invA) >> 8
97
95
 
98
96
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
99
97
  }
100
98
 
101
99
  /** src + dst - 255 */
102
- export const linearBurnColor32: BlendColor32 = (src, dst) => {
100
+ export const linearBurnFast: BlendColor32 = (src, dst) => {
103
101
  const sa = (src >>> 24) & 0xFF
104
102
  if (sa === 0) return dst
105
-
106
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
107
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
103
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
104
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
108
105
 
109
106
  // Math: Base + Blend - 255 (clamped to 0)
110
- const br = Math.max(0, dr + sr - 255)
111
- const bg = Math.max(0, dg + sg - 255)
112
- const bb = Math.max(0, db + sb - 255)
107
+ const brU = dr + sr - 255
108
+ const bgU = dg + sg - 255
109
+ const bbU = db + sb - 255
110
+
111
+ const br = brU < 0 ? 0 : brU
112
+ const bg = bgU < 0 ? 0 : bgU
113
+ const bb = bbU < 0 ? 0 : bbU
113
114
 
114
115
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
115
116
 
@@ -123,12 +124,12 @@ export const linearBurnColor32: BlendColor32 = (src, dst) => {
123
124
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
124
125
  }
125
126
 
126
- export const darkerColor32: BlendColor32 = (src, dst) => {
127
+ export const darkerFast: BlendColor32 = (src, dst) => {
127
128
  const sa = (src >>> 24) & 0xFF
128
129
  if (sa === 0) return dst
129
130
 
130
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
131
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
131
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
132
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
132
133
 
133
134
  // 1. Calculate Luminosity (Photoshop Weights: R:0.3, G:0.59, B:0.11)
134
135
  // Scaled by 256 for integer math: 77, 151, 28
@@ -161,7 +162,7 @@ export const darkerColor32: BlendColor32 = (src, dst) => {
161
162
  }
162
163
 
163
164
  /** Math.max(src, dst) */
164
- export const lightenColor32: BlendColor32 = (src, dst) => {
165
+ export const lightenFast: BlendColor32 = (src, dst) => {
165
166
  const sa = (src >>> 24) & 0xFF
166
167
  if (sa === 0) return dst
167
168
  const br = Math.max(src & 0xFF, dst & 0xFF)
@@ -187,15 +188,15 @@ export const lightenColor32: BlendColor32 = (src, dst) => {
187
188
  /**
188
189
  * 255 - ((255 - src) * (255 - dst))
189
190
  */
190
- export const screenColor32: BlendColor32 = (src, dst) => {
191
+ export const screenFast: BlendColor32 = (src, dst) => {
191
192
  const sa = (src >>> 24) & 0xFF
192
193
  if (sa === 0) return dst
193
194
 
194
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
195
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
195
196
 
196
197
  const br = 255 - (((255 - (src & 0xFF)) * (255 - dr)) >> 8)
197
- const bg = 255 - (((255 - ((src >> 8) & 0xFF)) * (255 - dg)) >> 8)
198
- const bb = 255 - (((255 - ((src >> 16) & 0xFF)) * (255 - db)) >> 8)
198
+ const bg = 255 - (((255 - ((src >>> 8) & 0xFF)) * (255 - dg)) >> 8)
199
+ const bb = 255 - (((255 - ((src >>> 16) & 0xFF)) * (255 - db)) >> 8)
199
200
 
200
201
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
201
202
 
@@ -210,16 +211,16 @@ export const screenColor32: BlendColor32 = (src, dst) => {
210
211
  }
211
212
 
212
213
  /** src === 255 ? 255 : Math.min(255, (dst << 8) / (255 - src)) */
213
- export const colorDodgeColor32: BlendColor32 = (src, dst) => {
214
+ export const colorDodgeFast: BlendColor32 = (src, dst) => {
214
215
  const sa = (src >>> 24) & 0xFF
215
216
  if (sa === 0) return dst
216
217
 
217
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
218
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
218
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
219
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
219
220
 
220
- const br = sr === 255 ? 255 : Math.min(255, (dr << 8) / (255 - sr))
221
- const bg = sg === 255 ? 255 : Math.min(255, (dg << 8) / (255 - sg))
222
- const bb = sb === 255 ? 255 : Math.min(255, (db << 8) / (255 - sb))
221
+ const br = sr === 255 ? 255 : Math.min(255, ((dr << 8) / (255 - sr)) | 0)
222
+ const bg = sg === 255 ? 255 : Math.min(255, ((dg << 8) / (255 - sg)) | 0)
223
+ const bb = sb === 255 ? 255 : Math.min(255, ((db << 8) / (255 - sb)) | 0)
223
224
 
224
225
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
225
226
 
@@ -234,15 +235,18 @@ export const colorDodgeColor32: BlendColor32 = (src, dst) => {
234
235
  }
235
236
 
236
237
  /** src + dst */
237
- export const linearDodgeColor32: BlendColor32 = (src, dst) => {
238
+ export const linearDodgeFast: BlendColor32 = (src, dst) => {
238
239
  const sa = (src >>> 24) & 0xFF
239
240
  if (sa === 0) return dst
241
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
240
242
 
241
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
243
+ const brU = (src & 0xFF) + dr
244
+ const bgU = ((src >>> 8) & 0xFF) + dg
245
+ const bbU = ((src >>> 16) & 0xFF) + db
242
246
 
243
- const br = Math.min(255, (src & 0xFF) + dr)
244
- const bg = Math.min(255, ((src >> 8) & 0xFF) + dg)
245
- const bb = Math.min(255, ((src >> 16) & 0xFF) + db)
247
+ const br = brU > 255 ? 255 : brU
248
+ const bg = bgU > 255 ? 255 : bgU
249
+ const bb = bbU > 255 ? 255 : bbU
246
250
 
247
251
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
248
252
 
@@ -256,12 +260,12 @@ export const linearDodgeColor32: BlendColor32 = (src, dst) => {
256
260
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
257
261
  }
258
262
 
259
- export const lighterColor32: BlendColor32 = (src, dst) => {
263
+ export const lighterFast: BlendColor32 = (src, dst) => {
260
264
  const sa = (src >>> 24) & 0xFF
261
265
  if (sa === 0) return dst
262
266
 
263
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
264
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
267
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
268
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
265
269
 
266
270
  // Calculate Luminosity (Photoshop uses Weights: R:0.3, G:0.59, B:0.11)
267
271
  // We use integer math (scaled by 256) for speed.
@@ -293,12 +297,12 @@ export const lighterColor32: BlendColor32 = (src, dst) => {
293
297
  }
294
298
 
295
299
  /** src < 128 ? (2 * src * dst) : (255 - 2 * (255 - src) * (255 - dst)) */
296
- export const overlayColor32: BlendColor32 = (src, dst) => {
300
+ export const overlayFast: BlendColor32 = (src, dst) => {
297
301
  const sa = (src >>> 24) & 0xFF
298
302
  if (sa === 0) return dst
299
303
 
300
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
301
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
304
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
305
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
302
306
 
303
307
  const br = dr < 128 ? (2 * sr * dr) >> 8 : 255 - (2 * (255 - sr) * (255 - dr) >> 8)
304
308
  const bg = dg < 128 ? (2 * sg * dg) >> 8 : 255 - (2 * (255 - sg) * (255 - dg) >> 8)
@@ -316,13 +320,13 @@ export const overlayColor32: BlendColor32 = (src, dst) => {
316
320
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
317
321
  }
318
322
 
319
- /** ((255 - dst) * ((src * dst) >> 8) + dst * (255 - (((255 - src) * (255 - dst)) >> 8))) >> 8 */
320
- export const softLightColor32: BlendColor32 = (src, dst) => {
323
+ /** ((255 - dst) * ((src * dst) >> 8) + dst * (255 - (((255 - src) * (255 - dst)) >> 8))) >> 8 */
324
+ export const softLightFast: BlendColor32 = (src, dst) => {
321
325
  const sa = (src >>> 24) & 0xFF
322
326
  if (sa === 0) return dst
323
327
 
324
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
325
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
328
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
329
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
326
330
 
327
331
  const br = ((255 - dr) * ((sr * dr) >> 8) + dr * (255 - (((255 - sr) * (255 - dr)) >> 8))) >> 8
328
332
  const bg = ((255 - dg) * ((sg * dg) >> 8) + dg * (255 - (((255 - sg) * (255 - dg)) >> 8))) >> 8
@@ -341,12 +345,12 @@ export const softLightColor32: BlendColor32 = (src, dst) => {
341
345
  }
342
346
 
343
347
  /** If src < 128 (50% gray), Multiply; otherwise, Screen */
344
- export const hardLightColor32: BlendColor32 = (src, dst) => {
348
+ export const hardLightFast: BlendColor32 = (src, dst) => {
345
349
  const sa = (src >>> 24) & 0xFF
346
350
  if (sa === 0) return dst
347
351
 
348
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
349
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
352
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
353
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
350
354
 
351
355
  const br = sr < 128 ? (2 * sr * dr) >> 8 : 255 - ((2 * (255 - sr) * (255 - dr)) >> 8)
352
356
  const bg = sg < 128 ? (2 * sg * dg) >> 8 : 255 - ((2 * (255 - sg) * (255 - dg)) >> 8)
@@ -368,24 +372,16 @@ export const hardLightColor32: BlendColor32 = (src, dst) => {
368
372
  * If src < 128: Burn(dst, 2 * src)
369
373
  * If src >= 128: Dodge(dst, 2 * (src - 128))
370
374
  */
371
- export const vividLightColor32: BlendColor32 = (src, dst) => {
375
+ export const vividLightFast: BlendColor32 = (src, dst) => {
372
376
  const sa = (src >>> 24) & 0xFF
373
377
  if (sa === 0) return dst
374
378
 
375
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
376
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
377
-
378
- const br = sr < 128
379
- ? (sr === 0 ? 0 : Math.max(0, 255 - (((255 - dr) << 8) / (2 * sr))))
380
- : (sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr))))
379
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
380
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
381
381
 
382
- const bg = sg < 128
383
- ? (sg === 0 ? 0 : Math.max(0, 255 - (((255 - dg) << 8) / (2 * sg))))
384
- : (sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg))))
385
-
386
- const bb = sb < 128
387
- ? (sb === 0 ? 0 : Math.max(0, 255 - (((255 - db) << 8) / (2 * sb))))
388
- : (sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb))))
382
+ const br = sr < 128 ? (sr === 0 ? 0 : Math.max(0, 255 - (((255 - dr) << 8) / (2 * sr)) | 0)) : (sr === 255 ? 255 : Math.min(255, ((dr << 8) / (2 * (255 - sr))) | 0))
383
+ const bg = sg < 128 ? (sg === 0 ? 0 : Math.max(0, 255 - (((255 - dg) << 8) / (2 * sg)) | 0)) : (sg === 255 ? 255 : Math.min(255, ((dg << 8) / (2 * (255 - sg))) | 0))
384
+ const bb = sb < 128 ? (sb === 0 ? 0 : Math.max(0, 255 - (((255 - db) << 8) / (2 * sb)) | 0)) : (sb === 255 ? 255 : Math.min(255, ((db << 8) / (2 * (255 - sb))) | 0))
389
385
 
390
386
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
391
387
 
@@ -400,16 +396,20 @@ export const vividLightColor32: BlendColor32 = (src, dst) => {
400
396
  }
401
397
 
402
398
  /** dst + 2 * src - 255 (Clamped to 0-255) */
403
- export const linearLightColor32: BlendColor32 = (src, dst) => {
399
+ export const linearLightFast: BlendColor32 = (src, dst) => {
404
400
  const sa = (src >>> 24) & 0xFF
405
401
  if (sa === 0) return dst
406
402
 
407
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
408
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
403
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
404
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
405
+
406
+ const brU = dr + 2 * sr - 255
407
+ const bgU = dg + 2 * sg - 255
408
+ const bbU = db + 2 * sb - 255
409
409
 
410
- const br = Math.max(0, Math.min(255, dr + 2 * sr - 255))
411
- const bg = Math.max(0, Math.min(255, dg + 2 * sg - 255))
412
- const bb = Math.max(0, Math.min(255, db + 2 * sb - 255))
410
+ const br = brU < 0 ? 0 : brU > 255 ? 255 : brU
411
+ const bg = bgU < 0 ? 0 : bgU > 255 ? 255 : bgU
412
+ const bb = bbU < 0 ? 0 : bbU > 255 ? 255 : bbU
413
413
 
414
414
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
415
415
 
@@ -424,16 +424,16 @@ export const linearLightColor32: BlendColor32 = (src, dst) => {
424
424
  }
425
425
 
426
426
  /** src < 128 ? min(dst, 2 * src) : max(dst, 2 * (src - 128)) */
427
- export const pinLightColor32: BlendColor32 = (src, dst) => {
427
+ export const pinLightFast: BlendColor32 = (src, dst) => {
428
428
  const sa = (src >>> 24) & 0xFF
429
429
  if (sa === 0) return dst
430
430
 
431
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
432
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
431
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
432
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
433
433
 
434
- const br = sr < 128 ? Math.min(dr, 2 * sr) : Math.max(dr, 2 * (sr - 128))
435
- const bg = sg < 128 ? Math.min(dg, 2 * sg) : Math.max(dg, 2 * (sg - 128))
436
- const bb = sb < 128 ? Math.min(db, 2 * sb) : Math.max(db, 2 * (sb - 128))
434
+ const br = sr < 128 ? (dr < 2 * sr ? dr : 2 * sr) : (dr > 2 * sr - 256 ? dr : 2 * sr - 256)
435
+ const bg = sg < 128 ? (dg < 2 * sg ? dg : 2 * sg) : (dg > 2 * sg - 256 ? dg : 2 * sg - 256)
436
+ const bb = sb < 128 ? (db < 2 * sb ? db : 2 * sb) : (db > 2 * sb - 256 ? db : 2 * sb - 256)
437
437
 
438
438
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
439
439
 
@@ -448,24 +448,16 @@ export const pinLightColor32: BlendColor32 = (src, dst) => {
448
448
  }
449
449
 
450
450
  /** (Vivid Light logic forced to 0 or 255) */
451
- export const hardMixColor32: BlendColor32 = (src, dst) => {
451
+ export const hardMixFast: BlendColor32 = (src, dst) => {
452
452
  const sa = (src >>> 24) & 0xFF
453
453
  if (sa === 0) return dst
454
454
 
455
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
456
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
457
-
458
- const br = (sr < 128
459
- ? (sr === 0 ? 0 : Math.max(0, 255 - (((255 - dr) << 8) / (2 * sr))))
460
- : (sr === 255 ? 255 : Math.min(255, (dr << 8) / (2 * (255 - sr))))) < 128 ? 0 : 255
461
-
462
- const bg = (sg < 128
463
- ? (sg === 0 ? 0 : Math.max(0, 255 - (((255 - dg) << 8) / (2 * sg))))
464
- : (sg === 255 ? 255 : Math.min(255, (dg << 8) / (2 * (255 - sg))))) < 128 ? 0 : 255
455
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
456
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
465
457
 
466
- const bb = (sb < 128
467
- ? (sb === 0 ? 0 : Math.max(0, 255 - (((255 - db) << 8) / (2 * sb))))
468
- : (sb === 255 ? 255 : Math.min(255, (db << 8) / (2 * (255 - sb))))) < 128 ? 0 : 255
458
+ const br = (sr < 128 ? (sr === 0 ? 0 : Math.max(0, 255 - (((255 - dr) << 8) / (2 * sr)) | 0)) : (sr === 255 ? 255 : Math.min(255, ((dr << 8) / (2 * (255 - sr))) | 0))) < 128 ? 0 : 255
459
+ const bg = (sg < 128 ? (sg === 0 ? 0 : Math.max(0, 255 - (((255 - dg) << 8) / (2 * sg)) | 0)) : (sg === 255 ? 255 : Math.min(255, ((dg << 8) / (2 * (255 - sg))) | 0))) < 128 ? 0 : 255
460
+ const bb = (sb < 128 ? (sb === 0 ? 0 : Math.max(0, 255 - (((255 - db) << 8) / (2 * sb)) | 0)) : (sb === 255 ? 255 : Math.min(255, ((db << 8) / (2 * (255 - sb))) | 0))) < 128 ? 0 : 255
469
461
 
470
462
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
471
463
 
@@ -480,15 +472,19 @@ export const hardMixColor32: BlendColor32 = (src, dst) => {
480
472
  }
481
473
 
482
474
  /** Math.abs(src - dst) */
483
- export const differenceColor32: BlendColor32 = (src, dst) => {
475
+ export const differenceFast: BlendColor32 = (src, dst) => {
484
476
  const sa = (src >>> 24) & 0xFF
485
477
  if (sa === 0) return dst
486
478
 
487
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
479
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
488
480
 
489
- const br = Math.abs((src & 0xFF) - dr)
490
- const bg = Math.abs(((src >> 8) & 0xFF) - dg)
491
- const bb = Math.abs(((src >> 16) & 0xFF) - db)
481
+ const brD = (src & 0xFF) - dr
482
+ const bgD = ((src >>> 8) & 0xFF) - dg
483
+ const bbD = ((src >>> 16) & 0xFF) - db
484
+
485
+ const br = brD < 0 ? -brD : brD
486
+ const bg = bgD < 0 ? -bgD : bgD
487
+ const bb = bbD < 0 ? -bbD : bbD
492
488
 
493
489
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
494
490
 
@@ -503,12 +499,12 @@ export const differenceColor32: BlendColor32 = (src, dst) => {
503
499
  }
504
500
 
505
501
  /** dst + src - ((dst * src) >> 7) */
506
- export const exclusionColor32: BlendColor32 = (src, dst) => {
502
+ export const exclusionFast: BlendColor32 = (src, dst) => {
507
503
  const sa = (src >>> 24) & 0xFF
508
504
  if (sa === 0) return dst
509
505
 
510
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
511
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
506
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
507
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
512
508
 
513
509
  const br = dr + sr - ((dr * sr) >> 7)
514
510
  const bg = dg + sg - ((dg * sg) >> 7)
@@ -527,16 +523,20 @@ export const exclusionColor32: BlendColor32 = (src, dst) => {
527
523
  }
528
524
 
529
525
  /** Math.max(0, dst - src) */
530
- export const subtractColor32: BlendColor32 = (src, dst) => {
526
+ export const subtractFast: BlendColor32 = (src, dst) => {
531
527
  const sa = (src >>> 24) & 0xFF
532
528
  if (sa === 0) return dst
533
529
 
534
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
535
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
530
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
531
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
532
+
533
+ const brU = dr - sr
534
+ const bgU = dg - sg
535
+ const bbU = db - sb
536
536
 
537
- const br = Math.max(0, dr - sr)
538
- const bg = Math.max(0, dg - sg)
539
- const bb = Math.max(0, db - sb)
537
+ const br = brU < 0 ? 0 : brU
538
+ const bg = bgU < 0 ? 0 : bgU
539
+ const bb = bbU < 0 ? 0 : bbU
540
540
 
541
541
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
542
542
 
@@ -551,16 +551,16 @@ export const subtractColor32: BlendColor32 = (src, dst) => {
551
551
  }
552
552
 
553
553
  /** sr === 0 ? 255 : Math.min(255, (dr << 8) / sr) */
554
- export const divideColor32: BlendColor32 = (src, dst) => {
554
+ export const divideFast: BlendColor32 = (src, dst) => {
555
555
  const sa = (src >>> 24) & 0xFF
556
556
  if (sa === 0) return dst
557
557
 
558
- const dr = dst & 0xFF, dg = (dst >> 8) & 0xFF, db = (dst >> 16) & 0xFF
559
- const sr = src & 0xFF, sg = (src >> 8) & 0xFF, sb = (src >> 16) & 0xFF
558
+ const dr = dst & 0xFF, dg = (dst >>> 8) & 0xFF, db = (dst >>> 16) & 0xFF
559
+ const sr = src & 0xFF, sg = (src >>> 8) & 0xFF, sb = (src >>> 16) & 0xFF
560
560
 
561
- const br = sr === 0 ? 255 : Math.min(255, (dr << 8) / sr)
562
- const bg = sg === 0 ? 255 : Math.min(255, (dg << 8) / sg)
563
- const bb = sb === 0 ? 255 : Math.min(255, (db << 8) / sb)
561
+ const br = sr === 0 ? 255 : Math.min(255, ((dr << 8) / sr) | 0)
562
+ const bg = sg === 0 ? 255 : Math.min(255, ((dg << 8) / sg) | 0)
563
+ const bb = sb === 0 ? 255 : Math.min(255, ((db << 8) / sb) | 0)
564
564
 
565
565
  if (sa === 255) return (0xFF000000 | (bb << 16) | (bg << 8) | br) >>> 0 as Color32
566
566
 
@@ -574,95 +574,93 @@ export const divideColor32: BlendColor32 = (src, dst) => {
574
574
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
575
575
  }
576
576
 
577
- // The enum index IS the permanent ID.
578
- // do not change the order, Adding to it is ok.
579
- export enum BlendMode {
580
- overwrite,
581
- sourceOver,
582
-
583
- darken,
584
- multiply,
585
- colorBurn,
586
- linearBurn,
587
- darkerColor,
588
-
589
- lighten,
590
- screen,
591
- colorDodge,
592
- linearDodge,
593
- lighterColor,
594
-
595
- overlay,
596
- softLight,
597
- hardLight,
598
- vividLight,
599
- linearLight,
600
- pinLight,
601
- hardMix,
602
-
603
- difference,
604
- exclusion,
605
- subtract,
606
- divide,
607
- }
608
-
609
- const BLENDER_REGISTRY = [
610
- [BlendMode.overwrite, overwriteColor32],
611
- [BlendMode.sourceOver, sourceOverColor32],
612
-
613
- [BlendMode.darken, darkenColor32],
614
- [BlendMode.multiply, multiplyColor32],
615
- [BlendMode.colorBurn, colorBurnColor32],
616
- [BlendMode.linearBurn, linearBurnColor32],
617
- [BlendMode.darkerColor, darkerColor32],
618
-
619
- [BlendMode.lighten, lightenColor32],
620
- [BlendMode.screen, screenColor32],
621
- [BlendMode.colorDodge, colorDodgeColor32],
622
- [BlendMode.linearDodge, linearDodgeColor32],
623
- [BlendMode.lighterColor, lighterColor32],
624
-
625
- [BlendMode.overlay, overlayColor32],
626
- [BlendMode.softLight, softLightColor32],
627
- [BlendMode.hardLight, hardLightColor32],
628
- [BlendMode.vividLight, vividLightColor32],
629
- [BlendMode.linearLight, linearLightColor32],
630
- [BlendMode.pinLight, pinLightColor32],
631
- [BlendMode.hardMix, hardMixColor32],
632
-
633
- [BlendMode.difference, differenceColor32],
634
- [BlendMode.exclusion, exclusionColor32],
635
- [BlendMode.subtract, subtractColor32],
636
- [BlendMode.divide, divideColor32],
577
+ export const FAST_BLENDER_REGISTRY = [
578
+ [BlendMode.overwrite, overwriteFast],
579
+ [BlendMode.sourceOver, sourceOverFast],
580
+
581
+ [BlendMode.darken, darkenFast],
582
+ [BlendMode.multiply, multiplyFast],
583
+ [BlendMode.colorBurn, colorBurnFast],
584
+ [BlendMode.linearBurn, linearBurnFast],
585
+ [BlendMode.darkerColor, darkerFast],
586
+
587
+ [BlendMode.lighten, lightenFast],
588
+ [BlendMode.screen, screenFast],
589
+ [BlendMode.colorDodge, colorDodgeFast],
590
+ [BlendMode.linearDodge, linearDodgeFast],
591
+ [BlendMode.lighterColor, lighterFast],
592
+
593
+ [BlendMode.overlay, overlayFast],
594
+ [BlendMode.softLight, softLightFast],
595
+ [BlendMode.hardLight, hardLightFast],
596
+ [BlendMode.vividLight, vividLightFast],
597
+ [BlendMode.linearLight, linearLightFast],
598
+ [BlendMode.pinLight, pinLightFast],
599
+ [BlendMode.hardMix, hardMixFast],
600
+
601
+ [BlendMode.difference, differenceFast],
602
+ [BlendMode.exclusion, exclusionFast],
603
+ [BlendMode.subtract, subtractFast],
604
+ [BlendMode.divide, divideFast],
637
605
  ] as const
638
606
 
639
- export type RegisteredBlender = typeof BLENDER_REGISTRY[number][1]
640
- export type BlendModeIndex = number & { readonly __brandBlendModeIndex: unique symbol }
607
+ export type RegisteredFastBlender = typeof FAST_BLENDER_REGISTRY[number][1]
608
+ export type FastBlendModeIndex = number & { readonly __brandBlendModeIndex: unique symbol }
641
609
 
642
- export const COLOR_32_BLEND_MODES: BlendColor32[] = []
610
+ export const FAST_BLEND_MODES: BlendColor32[] = []
643
611
 
644
- for (const [index, blend] of BLENDER_REGISTRY) {
645
- COLOR_32_BLEND_MODES[index as BlendModeIndex] = blend
612
+ for (const [index, blend] of FAST_BLENDER_REGISTRY) {
613
+ FAST_BLEND_MODES[index as FastBlendModeIndex] = blend
646
614
  }
647
615
 
648
- export const COLOR_32_BLEND_TO_INDEX = new Map<RegisteredBlender, BlendModeIndex>(
649
- BLENDER_REGISTRY.map((entry, index) => {
616
+ export const FAST_BLEND_TO_INDEX = new Map<RegisteredFastBlender, FastBlendModeIndex>(
617
+ FAST_BLENDER_REGISTRY.map((entry, index) => {
650
618
  return [
651
619
  entry[1],
652
- index as BlendModeIndex,
620
+ index as FastBlendModeIndex,
653
621
  ]
654
622
  }),
655
623
  ) as {
656
- get: (blend: RegisteredBlender) => BlendModeIndex
624
+ get: (blend: RegisteredFastBlender) => FastBlendModeIndex
657
625
  }
658
626
 
659
- export const INDEX_TO_COLOR_32_BLEND = new Map<BlendModeIndex, RegisteredBlender>(
660
- BLENDER_REGISTRY.map((entry, index) => {
627
+ export const INDEX_TO_FAST_BLEND = new Map<FastBlendModeIndex, RegisteredFastBlender>(
628
+ FAST_BLENDER_REGISTRY.map((entry, index) => {
661
629
  return [
662
- index as BlendModeIndex,
630
+ index as FastBlendModeIndex,
663
631
  entry[1],
664
632
  ]
665
633
  }),
666
634
  ) as {
667
- get: (index: BlendModeIndex) => RegisteredBlender
635
+ get: (index: FastBlendModeIndex) => RegisteredFastBlender
668
636
  }
637
+
638
+ export type FastBlendModes = {
639
+ [K in keyof typeof BlendMode]: RegisteredFastBlender
640
+ }
641
+
642
+ export const FAST_BLEND_MODE_BY_NAME: FastBlendModes = {
643
+ overwrite: overwriteFast,
644
+ sourceOver: sourceOverFast,
645
+ darken: darkenFast,
646
+ multiply: multiplyFast,
647
+ colorBurn: colorBurnFast,
648
+ linearBurn: linearBurnFast,
649
+ darkerColor: darkerFast,
650
+ lighten: lightenFast,
651
+ screen: screenFast,
652
+ colorDodge: colorDodgeFast,
653
+ linearDodge: linearDodgeFast,
654
+ lighterColor: lighterFast,
655
+ overlay: overlayFast,
656
+ softLight: softLightFast,
657
+ hardLight: hardLightFast,
658
+ vividLight: vividLightFast,
659
+ linearLight: linearLightFast,
660
+ pinLight: pinLightFast,
661
+ hardMix: hardMixFast,
662
+ difference: differenceFast,
663
+ exclusion: exclusionFast,
664
+ subtract: subtractFast,
665
+ divide: divideFast,
666
+ } as const