pixel-data-js 0.25.2 → 0.26.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 (63) hide show
  1. package/dist/index.prod.cjs +1696 -1526
  2. package/dist/index.prod.cjs.map +1 -1
  3. package/dist/index.prod.d.ts +414 -311
  4. package/dist/index.prod.js +1676 -1519
  5. package/dist/index.prod.js.map +1 -1
  6. package/package.json +8 -9
  7. package/src/Algorithm/floodFillSelection.ts +49 -80
  8. package/src/Canvas/PixelCanvas.ts +1 -1
  9. package/src/Canvas/ReusableCanvas.ts +1 -1
  10. package/src/History/HistoryAction.ts +6 -5
  11. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +8 -10
  12. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +8 -10
  13. package/src/History/PixelMutator/mutatorApplyMask.ts +39 -0
  14. package/src/History/PixelMutator/{mutatorBlendPixelDataAlphaMask.ts → mutatorBlendAlphaMask.ts} +9 -9
  15. package/src/History/PixelMutator/{mutatorBlendPixelDataBinaryMask.ts → mutatorBlendBinaryMask.ts} +9 -9
  16. package/src/History/PixelMutator/mutatorBlendColor.ts +8 -9
  17. package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +51 -0
  18. package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +51 -0
  19. package/src/History/PixelMutator/{mutatorBlendPaintMask.ts → mutatorBlendColorPaintMask.ts} +3 -3
  20. package/src/History/PixelMutator/mutatorBlendMask.ts +43 -0
  21. package/src/History/PixelMutator/mutatorBlendPaintRect.ts +55 -0
  22. package/src/History/PixelMutator/mutatorBlendPixel.ts +2 -2
  23. package/src/History/PixelMutator/mutatorBlendPixelData.ts +8 -9
  24. package/src/History/PixelMutator/mutatorClear.ts +13 -11
  25. package/src/History/PixelMutator/mutatorFill.ts +2 -2
  26. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +3 -4
  27. package/src/History/PixelMutator/mutatorInvert.ts +8 -9
  28. package/src/History/PixelMutator.ts +20 -4
  29. package/src/History/PixelWriter.ts +11 -13
  30. package/src/Input/fileToImageData.ts +1 -1
  31. package/src/Internal/helpers.ts +4 -1
  32. package/src/Mask/applyBinaryMaskToAlphaMask.ts +8 -10
  33. package/src/Paint/PaintBuffer.ts +3 -3
  34. package/src/Paint/PaintBufferCanvasRenderer.ts +1 -1
  35. package/src/Paint/makePaintMask.ts +5 -5
  36. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +3 -3
  37. package/src/PixelData/applyAlphaMaskToPixelData.ts +14 -16
  38. package/src/PixelData/applyBinaryMaskToPixelData.ts +14 -16
  39. package/src/PixelData/applyMaskToPixelData.ts +22 -0
  40. package/src/PixelData/blendColorPixelData.ts +12 -15
  41. package/src/PixelData/blendColorPixelDataAlphaMask.ts +16 -16
  42. package/src/PixelData/blendColorPixelDataBinaryMask.ts +16 -16
  43. package/src/PixelData/blendColorPixelDataMask.ts +16 -0
  44. package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +30 -0
  45. package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +30 -0
  46. package/src/PixelData/blendColorPixelDataPaintMask.ts +35 -0
  47. package/src/PixelData/blendPixelData.ts +14 -16
  48. package/src/PixelData/blendPixelDataAlphaMask.ts +17 -19
  49. package/src/PixelData/blendPixelDataBinaryMask.ts +17 -19
  50. package/src/PixelData/blendPixelDataMask.ts +16 -0
  51. package/src/PixelData/blendPixelDataPaintBuffer.ts +1 -1
  52. package/src/PixelData/{clearPixelData.ts → clearPixelDataFast.ts} +2 -2
  53. package/src/PixelData/fillPixelDataBinaryMask.ts +8 -22
  54. package/src/PixelData/fillPixelDataFast.ts +2 -2
  55. package/src/PixelData/invertPixelData.ts +13 -16
  56. package/src/_types.ts +8 -7
  57. package/src/index.ts +41 -31
  58. package/dist/index.dev.cjs +0 -5419
  59. package/dist/index.dev.cjs.map +0 -1
  60. package/dist/index.dev.js +0 -5208
  61. package/dist/index.dev.js.map +0 -1
  62. package/src/Canvas/_constants.ts +0 -2
  63. package/src/PixelData/PixelBuffer32.ts +0 -28
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "pixel-data-js",
3
3
  "type": "module",
4
- "version": "0.25.2",
5
- "packageManager": "pnpm@10.30.0",
4
+ "version": "0.26.0",
5
+ "packageManager": "pnpm@10.33.0",
6
6
  "description": "JS Pixel and ImageData operations",
7
7
  "author": {
8
8
  "name": "Carl Olsen",
@@ -27,10 +27,6 @@
27
27
  ".": {
28
28
  "types": "./dist/index.prod.d.ts",
29
29
  "source": "./src/index.ts",
30
- "development": {
31
- "require": "./dist/index.dev.cjs",
32
- "import": "./dist/index.dev.js"
33
- },
34
30
  "production": {
35
31
  "require": "./dist/index.prod.cjs",
36
32
  "import": "./dist/index.prod.js"
@@ -49,6 +45,8 @@
49
45
  "scripts": {
50
46
  "build": "tsup",
51
47
  "test": "vitest --coverage --project unit",
48
+ "re-index": "tsx _scripts/re-index.ts",
49
+ "check": "npm run check-circ && npm run re-index && npm run sort && npm run typecheck",
52
50
  "test:build": "pnpm build && vitest run --project dist",
53
51
  "check-circ": "tsx _scripts/check-circular.ts",
54
52
  "test:mutation": "stryker run",
@@ -63,7 +61,7 @@
63
61
  "devDependencies": {
64
62
  "@clack/prompts": "^1.1.0",
65
63
  "@mitata/counters": "^0.0.8",
66
- "@napi-rs/canvas": "^0.1.93",
64
+ "@napi-rs/canvas": "^0.1.97",
67
65
  "@stryker-mutator/core": "^9.5.1",
68
66
  "@stryker-mutator/typescript-checker": "^9.5.1",
69
67
  "@stryker-mutator/vitest-runner": "^9.5.1",
@@ -83,13 +81,14 @@
83
81
  "sanitize-filename": "^1.6.3",
84
82
  "sharp": "^0.34.5",
85
83
  "simple-git": "^3.33.0",
84
+ "source-map": "^0.7.6",
86
85
  "tsup": "^8.5.1",
87
86
  "tsx": "^4.21.0",
88
- "typedoc": "^0.28.17",
87
+ "typedoc": "^0.28.18",
89
88
  "typedoc-plugin-mdn-links": "^5.1.1",
90
89
  "typedoc-rhineai-theme": "^1.2.0",
91
90
  "typescript": "^5.9.3",
92
- "unplugin-inline": "^1.9.0",
91
+ "unplugin-inline": "^1.14.0",
93
92
  "vite-tsconfig-paths": "^6.1.1",
94
93
  "vitest": "3.2.4"
95
94
  },
@@ -1,19 +1,12 @@
1
- import { type BinaryMaskRect, type Color32, type ImageDataLike, MaskType, type Rect } from '../_types'
1
+ import { type BinaryMaskRect, type Color32, MaskType, type Rect } from '../_types'
2
2
  import { colorDistance } from '../color'
3
3
  import { extractImageDataBuffer } from '../ImageData/extractImageDataBuffer'
4
4
  import type { PixelData } from '../PixelData/PixelData'
5
5
  import { trimMaskRectBounds } from '../Rect/trimMaskRectBounds'
6
6
 
7
- export type FloodFillImageDataOptions = {
8
- contiguous?: boolean
9
- tolerance?: number
10
- bounds?: Rect
11
- }
12
-
13
- export type FloodFillResult = {
7
+ export type FloodFillResult = BinaryMaskRect & {
14
8
  startX: number
15
9
  startY: number
16
- selectionRect: BinaryMaskRect
17
10
  pixels: Uint8ClampedArray
18
11
  }
19
12
 
@@ -23,16 +16,15 @@ export type FloodFillResult = {
23
16
  * color tolerance. It can operate in "contiguous" mode (classic bucket fill) or
24
17
  * "non-contiguous" mode (selects all matching pixels in the buffer).
25
18
  *
26
- * @param img - The source image data to process.
19
+ * @param target - The source image data to process.
27
20
  * @param startX - The starting horizontal coordinate.
28
21
  * @param startY - The starting vertical coordinate.
29
- * @param options - Configuration for the fill operation.
30
- * @param options.contiguous - If true, only connected pixels are
22
+ * @param contiguous - If true, only connected pixels are
31
23
  * selected. If false, all pixels within tolerance are selected regardless of position.
32
- * @param options.tolerance - The maximum allowed difference in color
24
+ * @param tolerance - The maximum allowed difference in color
33
25
  * distance (0-255) for a pixel to be included.
34
- * @param options.bounds - Optional bounding box to restrict the search area.
35
- *
26
+ * @param bounds - Optional bounding box to restrict the search area.
27
+ * @param out output object
36
28
  * @returns A {@link FloodFillResult} containing the mask and bounds of the selection,
37
29
  * or `null` if the starting coordinates are out of bounds.
38
30
  *
@@ -50,49 +42,33 @@ export type FloodFillResult = {
50
42
  * ```
51
43
  */
52
44
  export function floodFillSelection(
53
- img: ImageDataLike | PixelData,
45
+ target: PixelData,
54
46
  startX: number,
55
47
  startY: number,
56
- {
57
- contiguous = true,
58
- tolerance = 0,
59
- bounds,
60
- }: FloodFillImageDataOptions = {},
48
+ contiguous = true,
49
+ tolerance = 0,
50
+ bounds?: Rect,
51
+ out?: FloodFillResult,
61
52
  ): FloodFillResult | null {
62
53
 
63
- let imageData: ImageDataLike
64
- let data32: Uint32Array
65
- if ('data32' in img) {
66
- data32 = img.data32
67
- imageData = img.imageData
68
- } else {
69
- data32 = new Uint32Array(
70
- img.data.buffer,
71
- img.data.byteOffset,
72
- img.data.byteLength >> 2,
73
- )
74
- imageData = img
75
- }
76
- const {
77
- width,
78
- height,
79
- } = img
80
-
81
- const limit = bounds || {
82
- x: 0,
83
- y: 0,
84
- w: width,
85
- h: height,
86
- }
54
+ const data32 = target.data32
55
+ const width = target.width
56
+ const height = target.height
57
+
58
+ const lx = bounds?.x ?? 0
59
+ const ly = bounds?.y ?? 0
60
+ const lw = bounds?.w ?? width
61
+ const lh = bounds?.h ?? height
87
62
 
88
- const xMin = Math.max(0, limit.x)
89
- const xMax = Math.min(width - 1, limit.x + limit.w - 1)
90
- const yMin = Math.max(0, limit.y)
91
- const yMax = Math.min(height - 1, limit.y + limit.h - 1)
63
+ const xMin = Math.max(0, lx)
64
+ const xMax = Math.min(width - 1, lx + lw - 1)
65
+ const yMin = Math.max(0, ly)
66
+ const yMax = Math.min(height - 1, ly + lh - 1)
92
67
 
93
68
  if (startX < xMin || startX > xMax || startY < yMin || startY > yMax) {
94
69
  return null
95
70
  }
71
+ out = out ?? {} as FloodFillResult
96
72
 
97
73
  const baseColor = data32[startY * width + startX] as Color32
98
74
 
@@ -178,50 +154,43 @@ export function floodFillSelection(
178
154
  }
179
155
  }
180
156
 
181
- if (matchCount === 0) {
182
- return null
183
- }
157
+ if (matchCount === 0) return null
158
+
184
159
  const w = maxX - minX + 1
185
160
  const h = maxY - minY + 1
186
- const selectionRect: BinaryMaskRect = {
187
- x: minX,
188
- y: minY,
189
- w,
190
- h,
191
- data: new Uint8Array(w * h),
192
- type: MaskType.BINARY,
193
- }
194
161
 
195
- const sw = selectionRect.w
196
- const sh = selectionRect.h
197
- const finalMask = selectionRect.data
162
+ out.startX = startX
163
+ out.startY = startY
164
+ out.x = minX
165
+ out.y = minY
166
+ out.w = w
167
+ out.h = h
168
+ out.data = new Uint8Array(w * h)
169
+ out.type = MaskType.BINARY
170
+
171
+ const finalMask = out.data
198
172
 
199
173
  for (let i = 0; i < matchCount; i++) {
200
- const mx = matchX[i] - selectionRect.x
201
- const my = matchY[i] - selectionRect.y
174
+ const mx = matchX[i] - minX
175
+ const my = matchY[i] - minY
202
176
 
203
- if (mx >= 0 && mx < sw && my >= 0 && my < sh) {
204
- finalMask[my * sw + mx] = 1
177
+ if (mx >= 0 && mx < w && my >= 0 && my < h) {
178
+ finalMask[my * w + mx] = 1
205
179
  }
206
180
  }
207
181
 
208
182
  trimMaskRectBounds(
209
- selectionRect,
183
+ out,
210
184
  { x: 0, y: 0, w: width, h: height },
211
185
  )
212
186
 
213
- const extracted = extractImageDataBuffer(
214
- imageData,
215
- selectionRect.x,
216
- selectionRect.y,
217
- selectionRect.w,
218
- selectionRect.h,
187
+ out.pixels = extractImageDataBuffer(
188
+ target.imageData,
189
+ out.x,
190
+ out.y,
191
+ out.w,
192
+ out.h,
219
193
  )
220
194
 
221
- return {
222
- startX,
223
- startY,
224
- selectionRect,
225
- pixels: extracted,
226
- }
195
+ return out
227
196
  }
@@ -1,4 +1,4 @@
1
- import { CANVAS_CTX_FAILED } from './_constants'
1
+ import { CANVAS_CTX_FAILED } from '../../support/error-strings'
2
2
 
3
3
  export type PixelCanvas = {
4
4
  readonly canvas: HTMLCanvasElement,
@@ -1,4 +1,4 @@
1
- import { CANVAS_CTX_FAILED } from './_constants'
1
+ import { CANVAS_CTX_FAILED } from '../../support/error-strings'
2
2
 
3
3
  export type CanvasContext<T> = T extends HTMLCanvasElement
4
4
  ? CanvasRenderingContext2D
@@ -1,5 +1,6 @@
1
+ import type { PixelAccumulator } from './PixelAccumulator'
2
+ import type { PixelEngineConfig } from './PixelEngineConfig'
1
3
  import { applyPatchTiles, type PixelPatchTiles } from './PixelPatchTiles'
2
- import type { PixelWriter } from './PixelWriter'
3
4
 
4
5
  export interface HistoryAction {
5
6
  undo: () => void
@@ -10,7 +11,8 @@ export interface HistoryAction {
10
11
  export type HistoryActionFactory = typeof makeHistoryAction
11
12
 
12
13
  export function makeHistoryAction(
13
- writer: PixelWriter<any>,
14
+ config: PixelEngineConfig,
15
+ accumulator: PixelAccumulator,
14
16
  patch: PixelPatchTiles,
15
17
  after?: () => void,
16
18
  afterUndo?: () => void,
@@ -18,9 +20,8 @@ export function makeHistoryAction(
18
20
  applyPatchTilesFn = applyPatchTiles,
19
21
  ): HistoryAction {
20
22
 
21
- const target = writer.config.target
22
- const tileSize = writer.config.tileSize
23
- const accumulator = writer.accumulator
23
+ const target = config.target
24
+ const tileSize = config.tileSize
24
25
 
25
26
  return {
26
27
  undo: () => {
@@ -1,6 +1,6 @@
1
- import { type AlphaMask, type ApplyMaskToPixelDataOptions, type HistoryMutator } from '../../_types'
1
+ import { type AlphaMask, type ApplyMaskToPixelDataOptions } from '../../_types'
2
2
  import { applyAlphaMaskToPixelData } from '../../PixelData/applyAlphaMaskToPixelData'
3
- import { PixelWriter } from '../PixelWriter'
3
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = {
6
6
  applyAlphaMaskToPixelData,
@@ -17,14 +17,12 @@ export const mutatorApplyAlphaMask = ((writer: PixelWriter<any>, deps: Deps = de
17
17
  } = deps
18
18
 
19
19
  return {
20
- applyAlphaMask(mask: AlphaMask, opts: ApplyMaskToPixelDataOptions = {}): boolean {
21
- let target = writer.config.target
22
- const {
23
- x = 0,
24
- y = 0,
25
- w = target.width,
26
- h = target.height,
27
- } = opts
20
+ applyAlphaMask(mask: AlphaMask, opts?: ApplyMaskToPixelDataOptions): boolean {
21
+ const target = writer.config.target
22
+ const x = opts?.x ?? 0
23
+ const y = opts?.y ?? 0
24
+ const w = opts?.w ?? target.width
25
+ const h = opts?.h ?? target.height
28
26
 
29
27
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
30
28
  return didChange(applyAlphaMaskToPixelData(target, mask, opts))
@@ -1,6 +1,6 @@
1
- import { type ApplyMaskToPixelDataOptions, type BinaryMask, type HistoryMutator } from '../../_types'
1
+ import { type ApplyMaskToPixelDataOptions, type BinaryMask } from '../../_types'
2
2
  import { applyBinaryMaskToPixelData } from '../../PixelData/applyBinaryMaskToPixelData'
3
- import { PixelWriter } from '../PixelWriter'
3
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = {
6
6
  applyBinaryMaskToPixelData,
@@ -17,14 +17,12 @@ export const mutatorApplyBinaryMask = ((writer: PixelWriter<any>, deps: Deps = d
17
17
  } = deps
18
18
 
19
19
  return {
20
- applyBinaryMask(mask: BinaryMask, opts: ApplyMaskToPixelDataOptions = {}): boolean {
21
- let target = writer.config.target
22
- const {
23
- x = 0,
24
- y = 0,
25
- w = target.width,
26
- h = target.height,
27
- } = opts
20
+ applyBinaryMask(mask: BinaryMask, opts?: ApplyMaskToPixelDataOptions): boolean {
21
+ const target = writer.config.target
22
+ const x = opts?.x ?? 0
23
+ const y = opts?.y ?? 0
24
+ const w = opts?.w ?? target.width
25
+ const h = opts?.h ?? target.height
28
26
 
29
27
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
30
28
  return didChange(applyBinaryMaskToPixelData(target, mask, opts))
@@ -0,0 +1,39 @@
1
+ import { type ApplyMaskToPixelDataOptions, type Mask, MaskType } from '../../_types'
2
+ import { applyAlphaMaskToPixelData } from '../../PixelData/applyAlphaMaskToPixelData'
3
+ import { applyBinaryMaskToPixelData } from '../../PixelData/applyBinaryMaskToPixelData'
4
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
5
+
6
+ const defaults = {
7
+ applyBinaryMaskToPixelData,
8
+ applyAlphaMaskToPixelData,
9
+ }
10
+
11
+ type Deps = Partial<typeof defaults>
12
+
13
+ /**
14
+ * @param deps - @hidden
15
+ */
16
+ export const mutatorApplyMask = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
17
+ const {
18
+ applyBinaryMaskToPixelData = defaults.applyBinaryMaskToPixelData,
19
+ applyAlphaMaskToPixelData = defaults.applyAlphaMaskToPixelData,
20
+ } = deps
21
+
22
+ return {
23
+ applyMask(mask: Mask, opts?: ApplyMaskToPixelDataOptions): boolean {
24
+ const target = writer.config.target
25
+ const x = opts?.x ?? 0
26
+ const y = opts?.y ?? 0
27
+ const w = opts?.w ?? target.width
28
+ const h = opts?.h ?? target.height
29
+
30
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
31
+
32
+ if (mask.type === MaskType.BINARY) {
33
+ return didChange(applyBinaryMaskToPixelData(target, mask, opts))
34
+ } else {
35
+ return didChange(applyAlphaMaskToPixelData(target, mask, opts))
36
+ }
37
+ },
38
+ }
39
+ }) satisfies HistoryMutator<any, Deps>
@@ -1,6 +1,6 @@
1
- import type { AlphaMask, HistoryMutator, IPixelData32, PixelBlendMaskOptions } from '../../_types'
1
+ import type { AlphaMask, IPixelData32, PixelBlendMaskOptions } from '../../_types'
2
2
  import { blendPixelDataAlphaMask } from '../../PixelData/blendPixelDataAlphaMask'
3
- import { PixelWriter } from '../PixelWriter'
3
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = { blendPixelDataAlphaMask }
6
6
  type Deps = Partial<typeof defaults>
@@ -8,21 +8,21 @@ type Deps = Partial<typeof defaults>
8
8
  /**
9
9
  * @param deps - @hidden
10
10
  */
11
- export const mutatorBlendPixelDataAlphaMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
11
+ export const mutatorBlendAlphaMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
12
12
  const {
13
13
  blendPixelDataAlphaMask = defaults.blendPixelDataAlphaMask,
14
14
  } = deps
15
15
 
16
16
  return {
17
- blendPixelDataAlphaMask(
17
+ blendAlphaMask(
18
18
  src: IPixelData32,
19
19
  mask: AlphaMask,
20
- opts: PixelBlendMaskOptions = {},
20
+ opts?: PixelBlendMaskOptions,
21
21
  ): boolean {
22
- const x = opts.x ?? 0
23
- const y = opts.y ?? 0
24
- const w = opts.w ?? src.width
25
- const h = opts.h ?? src.height
22
+ const x = opts?.x ?? 0
23
+ const y = opts?.y ?? 0
24
+ const w = opts?.w ?? src.width
25
+ const h = opts?.h ?? src.height
26
26
 
27
27
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
28
28
 
@@ -1,6 +1,6 @@
1
- import type { BinaryMask, HistoryMutator, IPixelData32, PixelBlendMaskOptions } from '../../_types'
1
+ import type { BinaryMask, IPixelData32, PixelBlendMaskOptions } from '../../_types'
2
2
  import { blendPixelDataBinaryMask } from '../../PixelData/blendPixelDataBinaryMask'
3
- import { PixelWriter } from '../PixelWriter'
3
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = { blendPixelDataBinaryMask }
6
6
  type Deps = Partial<typeof defaults>
@@ -8,21 +8,21 @@ type Deps = Partial<typeof defaults>
8
8
  /**
9
9
  * @param deps - @hidden
10
10
  */
11
- export const mutatorBlendPixelDataBinaryMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
11
+ export const mutatorBlendBinaryMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
12
12
  const {
13
13
  blendPixelDataBinaryMask = defaults.blendPixelDataBinaryMask,
14
14
  } = deps
15
15
 
16
16
  return {
17
- blendPixelDataBinaryMask(
17
+ blendBinaryMask(
18
18
  src: IPixelData32,
19
19
  mask: BinaryMask,
20
- opts: PixelBlendMaskOptions = {},
20
+ opts?: PixelBlendMaskOptions,
21
21
  ): boolean {
22
- const x = opts.x ?? 0
23
- const y = opts.y ?? 0
24
- const w = opts.w ?? src.width
25
- const h = opts.h ?? src.height
22
+ const x = opts?.x ?? 0
23
+ const y = opts?.y ?? 0
24
+ const w = opts?.w ?? src.width
25
+ const h = opts?.h ?? src.height
26
26
 
27
27
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
28
28
 
@@ -1,6 +1,6 @@
1
- import type { Color32, ColorBlendOptions, HistoryMutator } from '../../_types'
1
+ import type { Color32, ColorBlendOptions } from '../../_types'
2
2
  import { blendColorPixelData } from '../../PixelData/blendColorPixelData'
3
- import { PixelWriter } from '../PixelWriter'
3
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = { blendColorPixelData }
6
6
  type Deps = Partial<typeof defaults>
@@ -16,15 +16,14 @@ export const mutatorBlendColor = ((writer: PixelWriter<any>, deps: Deps = defaul
16
16
  return {
17
17
  blendColor(
18
18
  color: Color32,
19
- opts: ColorBlendOptions = {},
19
+ opts?: ColorBlendOptions,
20
20
  ): boolean {
21
21
  const target = writer.config.target
22
- const {
23
- x = 0,
24
- y = 0,
25
- w = target.width,
26
- h = target.height,
27
- } = opts
22
+ const x = opts?.x ?? 0
23
+ const y = opts?.y ?? 0
24
+ const w = opts?.w ?? target.width
25
+ const h = opts?.h ?? target.height
26
+
28
27
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
29
28
  return didChange(
30
29
  blendColorPixelData(target, color, opts),
@@ -0,0 +1,51 @@
1
+ import { type Color32, type PaintAlphaMask } from '../../_types'
2
+ import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
3
+ import { blendColorPixelDataAlphaMask } from '../../PixelData/blendColorPixelDataAlphaMask'
4
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
5
+
6
+ const defaults = {
7
+ blendColorPixelDataAlphaMask,
8
+ }
9
+ type Deps = Partial<typeof defaults>
10
+
11
+ /**
12
+ * @param deps - @hidden
13
+ */
14
+ export const mutatorBlendColorPaintAlphaMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
15
+ const {
16
+ blendColorPixelDataAlphaMask = defaults.blendColorPixelDataAlphaMask,
17
+ } = deps
18
+
19
+ const OPTS = {
20
+ x: 0,
21
+ y: 0,
22
+ blendFn: sourceOverPerfect,
23
+ alpha: 255,
24
+ }
25
+
26
+ return {
27
+ blendColorPaintAlphaMask(
28
+ color: Color32,
29
+ mask: PaintAlphaMask,
30
+ x: number,
31
+ y: number,
32
+ alpha = 255,
33
+ blendFn = sourceOverPerfect,
34
+ ): boolean {
35
+ const tx = x + mask.centerOffsetX
36
+ const ty = y + mask.centerOffsetY
37
+
38
+ const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h)
39
+
40
+ OPTS.x = tx
41
+ OPTS.y = ty
42
+ OPTS.alpha = alpha
43
+ OPTS.blendFn = blendFn
44
+
45
+ return didChange(
46
+ blendColorPixelDataAlphaMask(writer.config.target, color, mask, OPTS),
47
+ )
48
+ },
49
+ }
50
+ }) satisfies HistoryMutator<any, Deps>
51
+
@@ -0,0 +1,51 @@
1
+ import { type Color32, type PaintBinaryMask } from '../../_types'
2
+ import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
3
+ import { blendColorPixelDataBinaryMask } from '../../PixelData/blendColorPixelDataBinaryMask'
4
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
5
+
6
+ const defaults = {
7
+ blendColorPixelDataBinaryMask,
8
+ }
9
+ type Deps = Partial<typeof defaults>
10
+
11
+ /**
12
+ * @param deps - @hidden
13
+ */
14
+ export const mutatorBlendColorPaintBinaryMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
15
+ const {
16
+ blendColorPixelDataBinaryMask = defaults.blendColorPixelDataBinaryMask,
17
+ } = deps
18
+
19
+ const OPTS = {
20
+ x: 0,
21
+ y: 0,
22
+ blendFn: sourceOverPerfect,
23
+ alpha: 255,
24
+ }
25
+
26
+ return {
27
+ blendColorPaintBinaryMask(
28
+ color: Color32,
29
+ mask: PaintBinaryMask,
30
+ x: number,
31
+ y: number,
32
+ alpha = 255,
33
+ blendFn = sourceOverPerfect,
34
+ ): boolean {
35
+ const tx = x + mask.centerOffsetX
36
+ const ty = y + mask.centerOffsetY
37
+
38
+ const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h)
39
+
40
+ OPTS.x = tx
41
+ OPTS.y = ty
42
+ OPTS.alpha = alpha
43
+ OPTS.blendFn = blendFn
44
+
45
+ return didChange(
46
+ blendColorPixelDataBinaryMask(writer.config.target, color, mask, OPTS),
47
+ )
48
+ },
49
+ }
50
+ }) satisfies HistoryMutator<any, Deps>
51
+
@@ -1,8 +1,8 @@
1
- import { type Color32, type HistoryMutator, MaskType, type PaintMask } from '../../_types'
1
+ import { type Color32, MaskType, type PaintMask } from '../../_types'
2
2
  import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
3
3
  import { blendColorPixelDataAlphaMask } from '../../PixelData/blendColorPixelDataAlphaMask'
4
4
  import { blendColorPixelDataBinaryMask } from '../../PixelData/blendColorPixelDataBinaryMask'
5
- import { PixelWriter } from '../PixelWriter'
5
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
6
6
 
7
7
  const defaults = {
8
8
  blendColorPixelDataAlphaMask,
@@ -13,7 +13,7 @@ type Deps = Partial<typeof defaults>
13
13
  /**
14
14
  * @param deps - @hidden
15
15
  */
16
- export const mutatorBlendPaintMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
16
+ export const mutatorBlendColorPaintMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
17
17
  const {
18
18
  blendColorPixelDataBinaryMask = defaults.blendColorPixelDataBinaryMask,
19
19
  blendColorPixelDataAlphaMask = defaults.blendColorPixelDataAlphaMask,
@@ -0,0 +1,43 @@
1
+ import { type IPixelData32, type Mask, MaskType, type PixelBlendMaskOptions } from '../../_types'
2
+ import { blendPixelDataAlphaMask } from '../../PixelData/blendPixelDataAlphaMask'
3
+ import { blendPixelDataBinaryMask } from '../../PixelData/blendPixelDataBinaryMask'
4
+ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
5
+
6
+ const defaults = { blendPixelDataAlphaMask, blendPixelDataBinaryMask }
7
+ type Deps = Partial<typeof defaults>
8
+
9
+ /**
10
+ * @param deps - @hidden
11
+ */
12
+ export const mutatorBlendMask = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
13
+ const {
14
+ blendPixelDataAlphaMask = defaults.blendPixelDataAlphaMask,
15
+ blendPixelDataBinaryMask = defaults.blendPixelDataBinaryMask,
16
+ } = deps
17
+
18
+ return {
19
+ blendMask(
20
+ src: IPixelData32,
21
+ mask: Mask,
22
+ opts?: PixelBlendMaskOptions,
23
+ ): boolean {
24
+ const x = opts?.x ?? 0
25
+ const y = opts?.y ?? 0
26
+ const w = opts?.w ?? src.width
27
+ const h = opts?.h ?? src.height
28
+
29
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
30
+
31
+ if (mask.type === MaskType.BINARY) {
32
+ return didChange(
33
+ blendPixelDataBinaryMask(writer.config.target, src, mask, opts),
34
+ )
35
+ } else {
36
+ return didChange(
37
+ blendPixelDataAlphaMask(writer.config.target, src, mask, opts),
38
+ )
39
+ }
40
+ },
41
+ }
42
+ }) satisfies HistoryMutator<any, Deps>
43
+