pixel-data-js 0.15.0 → 0.16.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pixel-data-js",
3
3
  "type": "module",
4
- "version": "0.15.0",
4
+ "version": "0.16.0",
5
5
  "packageManager": "pnpm@10.30.0",
6
6
  "description": "JS Pixel and ImageData operations",
7
7
  "author": {
@@ -0,0 +1,78 @@
1
+ import type { BlendColor32 } from '../_types'
2
+
3
+ export type BlendModeRegistry = ReturnType<typeof makeBlendModeRegistry>
4
+
5
+ export function makeBlendModeRegistry<
6
+ BlendModes extends Record<string, number>,
7
+ Name extends keyof BlendModes = keyof BlendModes,
8
+ Index extends BlendModes[Name] = BlendModes[Name]
9
+ >(
10
+ blendModes: BlendModes,
11
+ initialEntries: Record<Index, BlendColor32>,
12
+ ) {
13
+
14
+ const modes: BlendColor32[] = []
15
+ const toIndex = new Map<BlendColor32, Index>()
16
+ const fromIndex = new Map<Index, BlendColor32>()
17
+ const byName = {} as Record<Name, BlendColor32>
18
+
19
+ const add = (name: Name, index: Index, blendFn: BlendColor32) => {
20
+ if (modes[index]) {
21
+ throw new Error(`Blend Mode index: ${index} is already used`)
22
+ }
23
+
24
+ if (byName[name]) {
25
+ throw new Error(`Blend Mode name: "${name as string}" is already used`)
26
+ }
27
+
28
+ const idx = index
29
+ modes[idx] = blendFn
30
+ toIndex.set(blendFn, idx)
31
+ fromIndex.set(idx, blendFn)
32
+ ;(byName as any)[name] = blendFn
33
+ }
34
+
35
+ for (const [name, index] of Object.entries(blendModes)) {
36
+ const blend = initialEntries[index as Index]
37
+ add(name as Name, index as Index, blend)
38
+ }
39
+
40
+ return {
41
+ modes,
42
+ byName,
43
+ toIndex,
44
+ fromIndex,
45
+ add,
46
+ indexType: null as unknown as Index,
47
+ nameType: null as unknown as Name
48
+ }
49
+ }
50
+
51
+ /**
52
+ * @internal
53
+ */
54
+ export interface BaseBlendModeRegistry {
55
+ overwrite: BlendColor32
56
+ sourceOver: BlendColor32
57
+ darken: BlendColor32
58
+ multiply: BlendColor32
59
+ colorBurn: BlendColor32
60
+ linearBurn: BlendColor32
61
+ darkerColor: BlendColor32
62
+ lighten: BlendColor32
63
+ screen: BlendColor32
64
+ colorDodge: BlendColor32
65
+ linearDodge: BlendColor32
66
+ lighterColor: BlendColor32
67
+ overlay: BlendColor32
68
+ softLight: BlendColor32
69
+ hardLight: BlendColor32
70
+ vividLight: BlendColor32
71
+ linearLight: BlendColor32
72
+ pinLight: BlendColor32
73
+ hardMix: BlendColor32
74
+ difference: BlendColor32
75
+ exclusion: BlendColor32
76
+ subtract: BlendColor32
77
+ divide: BlendColor32
78
+ }
@@ -1,6 +1,6 @@
1
1
  import type { BlendColor32, Color32 } from '../_types'
2
- import type { BaseBlendToIndexGetter, BaseIndexToBlendGetter } from './blend-mode-getters'
3
- import { BlendMode, type BlendModeIndex, overwriteBase } from './blend-modes'
2
+ import { BaseBlendMode, overwriteBase } from './blend-modes'
3
+ import { makeBlendModeRegistry } from './BlendModeRegistry'
4
4
 
5
5
  export const overwriteFast = overwriteBase
6
6
 
@@ -575,88 +575,35 @@ export const divideFast: BlendColor32 = (src, dst) => {
575
575
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
576
576
  }
577
577
 
578
- export const FAST_BLENDER_REGISTRY = [
579
- [BlendMode.overwrite, overwriteFast],
580
- [BlendMode.sourceOver, sourceOverFast],
581
-
582
- [BlendMode.darken, darkenFast],
583
- [BlendMode.multiply, multiplyFast],
584
- [BlendMode.colorBurn, colorBurnFast],
585
- [BlendMode.linearBurn, linearBurnFast],
586
- [BlendMode.darkerColor, darkerFast],
587
-
588
- [BlendMode.lighten, lightenFast],
589
- [BlendMode.screen, screenFast],
590
- [BlendMode.colorDodge, colorDodgeFast],
591
- [BlendMode.linearDodge, linearDodgeFast],
592
- [BlendMode.lighterColor, lighterFast],
593
-
594
- [BlendMode.overlay, overlayFast],
595
- [BlendMode.softLight, softLightFast],
596
- [BlendMode.hardLight, hardLightFast],
597
- [BlendMode.vividLight, vividLightFast],
598
- [BlendMode.linearLight, linearLightFast],
599
- [BlendMode.pinLight, pinLightFast],
600
- [BlendMode.hardMix, hardMixFast],
601
-
602
- [BlendMode.difference, differenceFast],
603
- [BlendMode.exclusion, exclusionFast],
604
- [BlendMode.subtract, subtractFast],
605
- [BlendMode.divide, divideFast],
606
- ] as const
607
-
608
- export type RegisteredFastBlender = typeof FAST_BLENDER_REGISTRY[number][1]
609
-
610
- export const FAST_BLEND_MODES: BlendColor32[] = []
611
-
612
- for (const [index, blend] of FAST_BLENDER_REGISTRY) {
613
- FAST_BLEND_MODES[index as BlendModeIndex] = blend
578
+ export const BASE_FAST_BLEND_MODE_FUNCTIONS: Record<number, BlendColor32> = {
579
+ [BaseBlendMode.overwrite]: overwriteFast,
580
+ [BaseBlendMode.sourceOver]: sourceOverFast,
581
+ [BaseBlendMode.darken]: darkenFast,
582
+ [BaseBlendMode.multiply]: multiplyFast,
583
+ [BaseBlendMode.colorBurn]: colorBurnFast,
584
+ [BaseBlendMode.linearBurn]: linearBurnFast,
585
+ [BaseBlendMode.darkerColor]: darkerFast,
586
+
587
+ [BaseBlendMode.lighten]: lightenFast,
588
+ [BaseBlendMode.screen]: screenFast,
589
+ [BaseBlendMode.colorDodge]: colorDodgeFast,
590
+ [BaseBlendMode.linearDodge]: linearDodgeFast,
591
+ [BaseBlendMode.lighterColor]: lighterFast,
592
+
593
+ [BaseBlendMode.overlay]: overlayFast,
594
+ [BaseBlendMode.softLight]: softLightFast,
595
+ [BaseBlendMode.hardLight]: hardLightFast,
596
+ [BaseBlendMode.vividLight]: vividLightFast,
597
+ [BaseBlendMode.linearLight]: linearLightFast,
598
+ [BaseBlendMode.pinLight]: pinLightFast,
599
+ [BaseBlendMode.hardMix]: hardMixFast,
600
+
601
+ [BaseBlendMode.difference]: differenceFast,
602
+ [BaseBlendMode.exclusion]: exclusionFast,
603
+ [BaseBlendMode.subtract]: subtractFast,
604
+ [BaseBlendMode.divide]: divideFast,
614
605
  }
615
606
 
616
- export const FAST_BLEND_TO_INDEX = new Map<RegisteredFastBlender, BlendModeIndex>(
617
- FAST_BLENDER_REGISTRY.map((entry, index) => {
618
- return [
619
- entry[1],
620
- index as BlendModeIndex,
621
- ]
622
- }),
623
- ) as BaseBlendToIndexGetter<RegisteredFastBlender>
624
-
625
- export const INDEX_TO_FAST_BLEND = new Map<BlendModeIndex, RegisteredFastBlender>(
626
- FAST_BLENDER_REGISTRY.map((entry, index) => {
627
- return [
628
- index as BlendModeIndex,
629
- entry[1],
630
- ]
631
- }),
632
- ) as BaseIndexToBlendGetter<RegisteredFastBlender>
633
-
634
- export type FastBlendModes = {
635
- [K in keyof typeof BlendMode]: RegisteredFastBlender
607
+ export function makeFastBlendModeRegistry() {
608
+ return makeBlendModeRegistry(BaseBlendMode, BASE_FAST_BLEND_MODE_FUNCTIONS)
636
609
  }
637
-
638
- export const FAST_BLEND_MODE_BY_NAME: FastBlendModes = {
639
- overwrite: overwriteFast,
640
- sourceOver: sourceOverFast,
641
- darken: darkenFast,
642
- multiply: multiplyFast,
643
- colorBurn: colorBurnFast,
644
- linearBurn: linearBurnFast,
645
- darkerColor: darkerFast,
646
- lighten: lightenFast,
647
- screen: screenFast,
648
- colorDodge: colorDodgeFast,
649
- linearDodge: linearDodgeFast,
650
- lighterColor: lighterFast,
651
- overlay: overlayFast,
652
- softLight: softLightFast,
653
- hardLight: hardLightFast,
654
- vividLight: vividLightFast,
655
- linearLight: linearLightFast,
656
- pinLight: pinLightFast,
657
- hardMix: hardMixFast,
658
- difference: differenceFast,
659
- exclusion: exclusionFast,
660
- subtract: subtractFast,
661
- divide: divideFast,
662
- } as const
@@ -1,6 +1,6 @@
1
1
  import type { BlendColor32, Color32 } from '../_types'
2
- import type { BaseBlendToIndexGetter, BaseIndexToBlendGetter } from './blend-mode-getters'
3
- import { BlendMode, type BlendModeIndex, overwriteBase } from './blend-modes'
2
+ import { BaseBlendMode, overwriteBase } from './blend-modes'
3
+ import { type BaseBlendModeRegistry, makeBlendModeRegistry } from './BlendModeRegistry'
4
4
 
5
5
  export const overwritePerfect = overwriteBase
6
6
 
@@ -565,87 +565,38 @@ export const dividePerfect: BlendColor32 = (src, dst) => {
565
565
  return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
566
566
  }
567
567
 
568
- export const PERFECT_BLENDER_REGISTRY = [
569
- [BlendMode.overwrite, overwritePerfect],
570
- [BlendMode.sourceOver, sourceOverPerfect],
571
-
572
- [BlendMode.darken, darkenPerfect],
573
- [BlendMode.multiply, multiplyPerfect],
574
- [BlendMode.colorBurn, colorBurnPerfect],
575
- [BlendMode.linearBurn, linearBurnPerfect],
576
- [BlendMode.darkerColor, darkerPerfect],
577
-
578
- [BlendMode.lighten, lightenPerfect],
579
- [BlendMode.screen, screenPerfect],
580
- [BlendMode.colorDodge, colorDodgePerfect],
581
- [BlendMode.linearDodge, linearDodgePerfect],
582
- [BlendMode.lighterColor, lighterPerfect],
583
-
584
- [BlendMode.overlay, overlayPerfect],
585
- [BlendMode.softLight, softLightPerfect],
586
- [BlendMode.hardLight, hardLightPerfect],
587
- [BlendMode.vividLight, vividLightPerfect],
588
- [BlendMode.linearLight, linearLightPerfect],
589
- [BlendMode.pinLight, pinLightPerfect],
590
- [BlendMode.hardMix, hardMixPerfect],
591
-
592
- [BlendMode.difference, differencePerfect],
593
- [BlendMode.exclusion, exclusionPerfect],
594
- [BlendMode.subtract, subtractPerfect],
595
- [BlendMode.divide, dividePerfect],
596
- ] as const
597
-
598
- export type RegisteredPerfectBlender = typeof PERFECT_BLENDER_REGISTRY[number][1]
599
-
600
- export const PERFECT_BLEND_MODES: BlendColor32[] = []
601
- for (const [index, blend] of PERFECT_BLENDER_REGISTRY) {
602
- PERFECT_BLEND_MODES[index as BlendModeIndex] = blend
568
+ export interface PerfectBlendModes extends BaseBlendModeRegistry {
603
569
  }
604
570
 
605
- export const PERFECT_BLEND_TO_INDEX = new Map(
606
- PERFECT_BLENDER_REGISTRY.map((entry, index) => {
607
- return [
608
- entry[1],
609
- index as BlendModeIndex,
610
- ]
611
- }),
612
- ) as BaseBlendToIndexGetter<RegisteredPerfectBlender>
613
-
614
- export const INDEX_TO_PERFECT_BLEND = new Map(
615
- PERFECT_BLENDER_REGISTRY.map((entry, index) => {
616
- return [
617
- index as BlendModeIndex,
618
- entry[1],
619
- ]
620
- }),
621
- ) as BaseIndexToBlendGetter<RegisteredPerfectBlender>
622
-
623
- export type PerfectBlendModes = {
624
- [K in keyof typeof BlendMode]: RegisteredPerfectBlender
571
+ export const BASE_PERFECT_BLEND_MODE_FUNCTIONS: Record<number, BlendColor32> = {
572
+ [BaseBlendMode.overwrite]: overwritePerfect,
573
+ [BaseBlendMode.sourceOver]: sourceOverPerfect,
574
+ [BaseBlendMode.darken]: darkenPerfect,
575
+ [BaseBlendMode.multiply]: multiplyPerfect,
576
+ [BaseBlendMode.colorBurn]: colorBurnPerfect,
577
+ [BaseBlendMode.linearBurn]: linearBurnPerfect,
578
+ [BaseBlendMode.darkerColor]: darkerPerfect,
579
+
580
+ [BaseBlendMode.lighten]: lightenPerfect,
581
+ [BaseBlendMode.screen]: screenPerfect,
582
+ [BaseBlendMode.colorDodge]: colorDodgePerfect,
583
+ [BaseBlendMode.linearDodge]: linearDodgePerfect,
584
+ [BaseBlendMode.lighterColor]: lighterPerfect,
585
+
586
+ [BaseBlendMode.overlay]: overlayPerfect,
587
+ [BaseBlendMode.softLight]: softLightPerfect,
588
+ [BaseBlendMode.hardLight]: hardLightPerfect,
589
+ [BaseBlendMode.vividLight]: vividLightPerfect,
590
+ [BaseBlendMode.linearLight]: linearLightPerfect,
591
+ [BaseBlendMode.pinLight]: pinLightPerfect,
592
+ [BaseBlendMode.hardMix]: hardMixPerfect,
593
+
594
+ [BaseBlendMode.difference]: differencePerfect,
595
+ [BaseBlendMode.exclusion]: exclusionPerfect,
596
+ [BaseBlendMode.subtract]: subtractPerfect,
597
+ [BaseBlendMode.divide]: dividePerfect,
625
598
  }
626
599
 
627
- export const PERFECT_BLEND_MODE_BY_NAME: PerfectBlendModes = {
628
- overwrite: overwritePerfect,
629
- sourceOver: sourceOverPerfect,
630
- darken: darkenPerfect,
631
- multiply: multiplyPerfect,
632
- colorBurn: colorBurnPerfect,
633
- linearBurn: linearBurnPerfect,
634
- darkerColor: darkerPerfect,
635
- lighten: lightenPerfect,
636
- screen: screenPerfect,
637
- colorDodge: colorDodgePerfect,
638
- linearDodge: linearDodgePerfect,
639
- lighterColor: lighterPerfect,
640
- overlay: overlayPerfect,
641
- softLight: softLightPerfect,
642
- hardLight: hardLightPerfect,
643
- vividLight: vividLightPerfect,
644
- linearLight: linearLightPerfect,
645
- pinLight: pinLightPerfect,
646
- hardMix: hardMixPerfect,
647
- difference: differencePerfect,
648
- exclusion: exclusionPerfect,
649
- subtract: subtractPerfect,
650
- divide: dividePerfect,
651
- } as const
600
+ export function makePerfectBlendModeRegistry() {
601
+ return makeBlendModeRegistry(BaseBlendMode, BASE_PERFECT_BLEND_MODE_FUNCTIONS)
602
+ }
@@ -1,38 +1,30 @@
1
- // The enum index IS the permanent ID.
2
- // do not change the order, Adding to it is ok.
3
1
  import type { BlendColor32 } from '../_types'
4
2
 
5
- export enum BlendMode {
6
- overwrite,
7
- sourceOver,
8
-
9
- darken,
10
- multiply,
11
- colorBurn,
12
- linearBurn,
13
- darkerColor,
14
-
15
- lighten,
16
- screen,
17
- colorDodge,
18
- linearDodge,
19
- lighterColor,
20
-
21
- overlay,
22
- softLight,
23
- hardLight,
24
- vividLight,
25
- linearLight,
26
- pinLight,
27
- hardMix,
28
-
29
- difference,
30
- exclusion,
31
- subtract,
32
- divide,
33
- }
34
-
35
- export type BlendModeIndex = typeof BlendMode[keyof typeof BlendMode];
3
+ export const BaseBlendMode = {
4
+ overwrite: 0,
5
+ sourceOver: 1,
6
+ darken: 2,
7
+ multiply: 3,
8
+ colorBurn: 4,
9
+ linearBurn: 5,
10
+ darkerColor: 6,
11
+ lighten: 7,
12
+ screen: 8,
13
+ colorDodge: 9,
14
+ linearDodge: 10,
15
+ lighterColor: 11,
16
+ overlay: 12,
17
+ softLight: 13,
18
+ hardLight: 14,
19
+ vividLight: 15,
20
+ linearLight: 16,
21
+ pinLight: 17,
22
+ hardMix: 18,
23
+ difference: 19,
24
+ exclusion: 20,
25
+ subtract: 21,
26
+ divide: 22,
27
+ } as const
36
28
 
37
29
  export const overwriteBase: BlendColor32 = (src, _dst) => src
38
30
  overwriteBase.isOverwrite = true
@@ -1,6 +1,5 @@
1
1
  import { type Color32, MaskType, type PixelBlendOptions } from '../_types'
2
- import { BlendMode } from '../BlendModes/blend-modes'
3
- import { FAST_BLEND_MODES } from '../BlendModes/blend-modes-fast'
2
+ import { sourceOverFast } from '../BlendModes/blend-modes-fast'
4
3
  import type { PixelData } from './PixelData'
5
4
 
6
5
  /**
@@ -30,7 +29,7 @@ export function blendPixelData(
30
29
  w: width = src.width,
31
30
  h: height = src.height,
32
31
  alpha: globalAlpha = 255,
33
- blendFn = FAST_BLEND_MODES[BlendMode.sourceOver],
32
+ blendFn = sourceOverFast,
34
33
  mask,
35
34
  maskType = MaskType.ALPHA,
36
35
  mw,
@@ -0,0 +1,28 @@
1
+ import type { Rect } from '../_types'
2
+ import { extractPixelDataBuffer } from './extractPixelDataBuffer'
3
+ import { PixelData } from './PixelData'
4
+
5
+ /**
6
+ * High-level extraction that returns a new PixelData instance.
7
+ * Leverages extractPixelDataBuffer for optimized 32-bit memory moves.
8
+ */
9
+ export function extractPixelData(source: PixelData, rect: Rect): PixelData
10
+ export function extractPixelData(source: PixelData, x: number, y: number, w: number, h: number): PixelData
11
+ export function extractPixelData(
12
+ source: PixelData,
13
+ _x: Rect | number,
14
+ _y?: number,
15
+ _w?: number,
16
+ _h?: number,
17
+ ): PixelData {
18
+ const { x, y, w, h } = typeof _x === 'object'
19
+ ? _x
20
+ : { x: _x, y: _y!, w: _w!, h: _h! }
21
+
22
+ const result = new PixelData(new ImageData(w, h))
23
+
24
+ const buffer = extractPixelDataBuffer(source, x, y, w, h)
25
+ result.data32.set(buffer)
26
+
27
+ return result
28
+ }
package/src/index.ts CHANGED
@@ -3,15 +3,8 @@ export * from './color'
3
3
 
4
4
  export * from './Algorithm/floodFillSelection'
5
5
 
6
- export {
7
- BlendMode,
8
- type BlendModeIndex,
9
- } from './BlendModes/blend-modes'
10
- export {
11
- type BlendToIndexGetter,
12
- type IndexToBlendGetter,
13
- } from './BlendModes/blend-mode-getters'
14
-
6
+ export * from './BlendModes/BlendModeRegistry'
7
+ export * from './BlendModes/blend-modes'
15
8
  export * from './BlendModes/blend-modes-fast'
16
9
  export * from './BlendModes/blend-modes-perfect'
17
10
 
@@ -57,6 +50,7 @@ export * from './PixelData/applyMaskToPixelData'
57
50
  export * from './PixelData/blendColorPixelData'
58
51
  export * from './PixelData/blendPixelData'
59
52
  export * from './PixelData/clearPixelData'
53
+ export * from './PixelData/extractPixelData'
60
54
  export * from './PixelData/extractPixelDataBuffer'
61
55
  export * from './PixelData/fillPixelData'
62
56
  export * from './PixelData/invertPixelData'
@@ -1,14 +0,0 @@
1
- import type { BlendColor32 } from '../_types'
2
- import type { BlendModeIndex } from './blend-modes'
3
- import { FAST_BLEND_TO_INDEX, INDEX_TO_FAST_BLEND } from './blend-modes-fast'
4
- import { type INDEX_TO_PERFECT_BLEND, PERFECT_BLEND_TO_INDEX } from './blend-modes-perfect'
5
-
6
- export type BaseIndexToBlendGetter<B extends BlendColor32> = {
7
- get: (index: BlendModeIndex) => B
8
- }
9
- export type IndexToBlendGetter = typeof INDEX_TO_FAST_BLEND | typeof INDEX_TO_PERFECT_BLEND
10
-
11
- export type BaseBlendToIndexGetter<B extends BlendColor32> = {
12
- get: (blend: B) => BlendModeIndex
13
- }
14
- export type BlendToIndexGetter = typeof FAST_BLEND_TO_INDEX | typeof PERFECT_BLEND_TO_INDEX