pixel-data-js 0.20.0 → 0.22.2

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 (65) hide show
  1. package/dist/index.dev.cjs +722 -357
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +709 -356
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +722 -357
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +283 -128
  8. package/dist/index.prod.js +709 -356
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Algorithm/floodFillSelection.ts +12 -14
  12. package/src/BlendModes/toBlendModeIndexAndName.ts +0 -7
  13. package/src/Clipboard/writeImgBlobToClipboard.ts +1 -1
  14. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +3 -0
  15. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +3 -0
  16. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +25 -6
  17. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +89 -46
  18. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +7 -7
  19. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +81 -41
  20. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +3 -0
  21. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +18 -5
  22. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +3 -0
  23. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +19 -4
  24. package/src/History/PixelMutator/mutatorBlendColor.ts +4 -0
  25. package/src/History/PixelMutator/mutatorBlendPixelData.ts +4 -0
  26. package/src/History/PixelMutator/mutatorClear.ts +11 -8
  27. package/src/History/PixelMutator/mutatorFill.ts +4 -0
  28. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +28 -0
  29. package/src/History/PixelMutator/mutatorInvert.ts +3 -0
  30. package/src/ImageData/extractImageDataBuffer.ts +3 -3
  31. package/src/ImageData/{imageDataToAlphaMask.ts → imageDataToAlphaMaskBuffer.ts} +3 -4
  32. package/src/ImageData/resizeImageData.ts +3 -5
  33. package/src/ImageData/writeImageDataBuffer.ts +7 -7
  34. package/src/Mask/AlphaMask.ts +16 -0
  35. package/src/Mask/BinaryMask.ts +16 -0
  36. package/src/Mask/CircleBrushAlphaMask.ts +32 -0
  37. package/src/Mask/CircleBrushBinaryMask.ts +30 -0
  38. package/src/Mask/applyBinaryMaskToAlphaMask.ts +12 -9
  39. package/src/Mask/copyMask.ts +9 -3
  40. package/src/Mask/extractMask.ts +33 -31
  41. package/src/Mask/extractMaskBuffer.ts +87 -0
  42. package/src/Mask/invertMask.ts +6 -4
  43. package/src/Mask/mergeAlphaMasks.ts +11 -10
  44. package/src/Mask/mergeBinaryMasks.ts +10 -9
  45. package/src/Mask/setMaskData.ts +7 -0
  46. package/src/MaskRect/merge2BinaryMaskRects.ts +81 -0
  47. package/src/MaskRect/mergeBinaryMaskRects.ts +39 -0
  48. package/src/MaskRect/subtractBinaryMaskRects.ts +80 -0
  49. package/src/PixelData/applyAlphaMaskToPixelData.ts +8 -5
  50. package/src/PixelData/applyBinaryMaskToPixelData.ts +8 -9
  51. package/src/PixelData/applyCircleBrushToPixelData.ts +54 -62
  52. package/src/PixelData/blendColorPixelDataAlphaMask.ts +35 -25
  53. package/src/PixelData/blendColorPixelDataBinaryMask.ts +26 -19
  54. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  55. package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
  56. package/src/PixelData/clearPixelData.ts +2 -2
  57. package/src/PixelData/fillPixelData.ts +15 -42
  58. package/src/PixelData/fillPixelDataBinaryMask.ts +79 -0
  59. package/src/PixelData/invertPixelData.ts +3 -3
  60. package/src/PixelData/pixelDataToAlphaMask.ts +4 -2
  61. package/src/PixelData/writePixelDataBuffer.ts +2 -3
  62. package/src/Rect/getRectsBounds.ts +22 -0
  63. package/src/Rect/trimRectBounds.ts +20 -17
  64. package/src/_types.ts +55 -29
  65. package/src/index.ts +16 -1
@@ -3,7 +3,7 @@ import {
3
3
  type BlendColor32,
4
4
  type Color32,
5
5
  type ColorBlendOptions,
6
- type HistoryMutator,
6
+ type HistoryMutator, MaskType,
7
7
  type Rect,
8
8
  } from '../../_types'
9
9
  import { forEachLinePoint } from '../../Algorithm/forEachLinePoint'
@@ -22,6 +22,9 @@ const defaults = {
22
22
 
23
23
  type Deps = Partial<typeof defaults>
24
24
 
25
+ /**
26
+ * @param deps - @hidden
27
+ */
25
28
  export const mutatorApplyRectBrushStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
26
29
  const {
27
30
  forEachLinePoint = defaults.forEachLinePoint,
@@ -53,6 +56,13 @@ export const mutatorApplyRectBrushStroke = ((writer: PixelWriter<any>, deps: Dep
53
56
  h: 0,
54
57
  }
55
58
 
59
+ const mask = {
60
+ type: MaskType.ALPHA,
61
+ data: null as unknown as Uint8Array,
62
+ w: 0,
63
+ h: 0,
64
+ }
65
+
56
66
  return {
57
67
  applyRectBrushStroke(
58
68
  color: Color32,
@@ -83,8 +93,11 @@ export const mutatorApplyRectBrushStroke = ((writer: PixelWriter<any>, deps: Dep
83
93
 
84
94
  if (bw <= 0 || bh <= 0) return
85
95
 
86
- const mask = new Uint8Array(bw * bh) as AlphaMask
96
+ mask.data = new Uint8Array(bw * bh)
97
+ mask.w = bw
98
+ mask.h = bh
87
99
 
100
+ const maskData = mask.data
88
101
  const halfW = brushWidth / 2
89
102
  const halfH = brushHeight / 2
90
103
  const invHalfW = 1 / halfW
@@ -143,8 +156,8 @@ export const mutatorApplyRectBrushStroke = ((writer: PixelWriter<any>, deps: Dep
143
156
  if (strength > 0) {
144
157
  const intensity = (strength * 255) | 0
145
158
 
146
- if (intensity > mask[maskIdx]) {
147
- mask[maskIdx] = intensity
159
+ if (intensity > maskData[maskIdx]) {
160
+ maskData[maskIdx] = intensity
148
161
  }
149
162
  }
150
163
  }
@@ -161,7 +174,7 @@ export const mutatorApplyRectBrushStroke = ((writer: PixelWriter<any>, deps: Dep
161
174
  blendColorPixelDataAlphaMask(
162
175
  writer.target,
163
176
  color,
164
- mask,
177
+ mask as AlphaMask,
165
178
  blendColorPixelOptions,
166
179
  )
167
180
  },
@@ -11,6 +11,9 @@ const defaults = {
11
11
 
12
12
  type Deps = Partial<typeof defaults>
13
13
 
14
+ /**
15
+ * @param deps - @hidden
16
+ */
14
17
  export const mutatorApplyRectPencil = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
15
18
  const {
16
19
  applyRectBrushToPixelData = defaults.applyRectBrushToPixelData,
@@ -3,7 +3,7 @@ import {
3
3
  type BlendColor32,
4
4
  type Color32,
5
5
  type ColorBlendOptions,
6
- type HistoryMutator,
6
+ type HistoryMutator, MaskType,
7
7
  type Rect,
8
8
  } from '../../_types'
9
9
  import { forEachLinePoint } from '../../Algorithm/forEachLinePoint'
@@ -21,6 +21,10 @@ const defaults = {
21
21
  }
22
22
 
23
23
  type Deps = Partial<typeof defaults>
24
+
25
+ /**
26
+ * @param deps - @hidden
27
+ */
24
28
  export const mutatorApplyRectPencilStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
25
29
  const {
26
30
  forEachLinePoint = defaults.forEachLinePoint,
@@ -52,6 +56,13 @@ export const mutatorApplyRectPencilStroke = ((writer: PixelWriter<any>, deps: De
52
56
  h: 0,
53
57
  }
54
58
 
59
+ const mask = {
60
+ type: MaskType.BINARY,
61
+ data: null as unknown as Uint8Array,
62
+ w: 0,
63
+ h: 0,
64
+ }
65
+
55
66
  return {
56
67
  applyRectPencilStroke(
57
68
  color: Color32,
@@ -81,7 +92,11 @@ export const mutatorApplyRectPencilStroke = ((writer: PixelWriter<any>, deps: De
81
92
 
82
93
  if (bw <= 0 || bh <= 0) return
83
94
 
84
- const mask = new Uint8Array(bw * bh) as BinaryMask
95
+ mask.data = new Uint8Array(bw * bh)
96
+ mask.w = bw
97
+ mask.h = bh
98
+
99
+ const maskData = mask.data
85
100
 
86
101
  const halfW = brushWidth / 2
87
102
  const halfH = brushHeight / 2
@@ -130,7 +145,7 @@ export const mutatorApplyRectPencilStroke = ((writer: PixelWriter<any>, deps: De
130
145
  const maskIdx = maskRowOffset + (mx - bx)
131
146
 
132
147
  if (dx <= halfW && dy <= halfH) {
133
- mask[maskIdx] = 1
148
+ maskData[maskIdx] = 1
134
149
  }
135
150
  }
136
151
  }
@@ -143,7 +158,7 @@ export const mutatorApplyRectPencilStroke = ((writer: PixelWriter<any>, deps: De
143
158
  blendColorPixelOptions.w = bw
144
159
  blendColorPixelOptions.h = bh
145
160
 
146
- blendColorPixelDataBinaryMask(writer.target, color, mask, blendColorPixelOptions)
161
+ blendColorPixelDataBinaryMask(writer.target, color, mask as BinaryMask, blendColorPixelOptions)
147
162
  },
148
163
  }
149
164
  }) satisfies HistoryMutator<any, Deps>
@@ -4,6 +4,10 @@ import { PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = { blendColorPixelData }
6
6
  type Deps = Partial<typeof defaults>
7
+
8
+ /**
9
+ * @param deps - @hidden
10
+ */
7
11
  export const mutatorBlendColor = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
8
12
  const {
9
13
  blendColorPixelData = defaults.blendColorPixelData,
@@ -4,6 +4,10 @@ import { PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = { blendPixelData }
6
6
  type Deps = Partial<typeof defaults>
7
+
8
+ /**
9
+ * @param deps - @hidden
10
+ */
7
11
  export const mutatorBlendPixelData = ((writer: PixelWriter<any>, deps: Partial<Deps> = defaults) => {
8
12
  const {
9
13
  blendPixelData = defaults.blendPixelData,
@@ -1,10 +1,14 @@
1
- import type { Color32, HistoryMutator, Rect } from '../../_types'
1
+ import type { BinaryMaskRect, Color32, HistoryMutator } from '../../_types'
2
2
  import { fillPixelData } from '../../PixelData/fillPixelData'
3
3
  import { PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = { fillPixelData }
6
6
 
7
7
  type Deps = Partial<typeof defaults>
8
+
9
+ /**
10
+ * @param deps - @hidden
11
+ */
8
12
  export const mutatorClear = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
9
13
  const {
10
14
  fillPixelData = defaults.fillPixelData,
@@ -12,14 +16,13 @@ export const mutatorClear = ((writer: PixelWriter<any>, deps: Deps = defaults) =
12
16
 
13
17
  return {
14
18
  clear(
15
- rect: Partial<Rect> = {},
19
+ rect: Partial<BinaryMaskRect> = {},
16
20
  ) {
17
- const {
18
- x = 0,
19
- y = 0,
20
- w = writer.target.width,
21
- h = writer.target.height,
22
- } = rect
21
+ const x = rect.x ?? 0
22
+ const y = rect.y ?? 0
23
+ const w = rect.w ?? writer.target.width
24
+ const h = rect.h ?? writer.target.height
25
+
23
26
  writer.accumulator.storeRegionBeforeState(x, y, w, h)
24
27
  fillPixelData(writer.target, 0 as Color32, x, y, w, h)
25
28
  },
@@ -4,6 +4,10 @@ import { PixelWriter } from '../PixelWriter'
4
4
 
5
5
  const defaults = { fillPixelData }
6
6
  type Deps = Partial<typeof defaults>
7
+
8
+ /**
9
+ * @param deps - @hidden
10
+ */
7
11
  export const mutatorFill = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
8
12
  const {
9
13
  fillPixelData = defaults.fillPixelData,
@@ -0,0 +1,28 @@
1
+ import type { BinaryMask, Color32, HistoryMutator } from '../../_types'
2
+ import { fillPixelDataBinaryMask } from '../../PixelData/fillPixelDataBinaryMask'
3
+ import { PixelWriter } from '../PixelWriter'
4
+
5
+ const defaults = { fillPixelDataBinaryMask }
6
+ type Deps = Partial<typeof defaults>
7
+
8
+ /**
9
+ * @param deps - @hidden
10
+ */
11
+ export const mutatorFillBinaryMask = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
12
+ const {
13
+ fillPixelDataBinaryMask = defaults.fillPixelDataBinaryMask,
14
+ } = deps
15
+
16
+ return {
17
+ fillBinaryMask(
18
+ color: Color32,
19
+ mask: BinaryMask,
20
+ alpha = 255,
21
+ x = 0,
22
+ y = 0,
23
+ ) {
24
+ writer.accumulator.storeRegionBeforeState(x, y, mask.w, mask.h)
25
+ fillPixelDataBinaryMask(writer.target, color, mask, alpha, x, y)
26
+ },
27
+ }
28
+ }) satisfies HistoryMutator<any, Deps>
@@ -5,6 +5,9 @@ import { PixelWriter } from '../PixelWriter'
5
5
  const defaults = { invertPixelData }
6
6
  type Deps = Partial<typeof defaults>
7
7
 
8
+ /**
9
+ * @param deps - @hidden
10
+ */
8
11
  export const mutatorInvert = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
9
12
  const {
10
13
  invertPixelData = defaults.invertPixelData,
@@ -10,8 +10,8 @@ const SCRATCH_BLIT = makeClippedBlit()
10
10
  * This is a "read-only" operation that returns a copy of the pixel data.
11
11
  *
12
12
  * @param imageData - The source image data to read from.
13
- * @param rect - A {@link Rect} object defining the region to extract.
14
- * @returns A {@link Uint8ClampedArray} containing the RGBA pixel data of the region.
13
+ * @param rect - A rect defining the region to extract.
14
+ * @returns A buffer containing the RGBA pixel data of the region.
15
15
  */
16
16
  export function extractImageDataBuffer(
17
17
  imageData: ImageDataLike,
@@ -23,7 +23,7 @@ export function extractImageDataBuffer(
23
23
  * @param y - The starting vertical coordinate.
24
24
  * @param w - The width of the region to extract.
25
25
  * @param h - The height of the region to extract.
26
- * @returns A {@link Uint8ClampedArray} containing the RGBA pixel data of the region.
26
+ * @returns A buffer containing the RGBA pixel data of the region.
27
27
  */
28
28
  export function extractImageDataBuffer(
29
29
  imageData: ImageDataLike,
@@ -1,4 +1,3 @@
1
- import type { AlphaMask } from '../_types'
2
1
  import { pixelDataToAlphaMask } from '../PixelData/pixelDataToAlphaMask'
3
2
 
4
3
  /**
@@ -6,9 +5,9 @@ import { pixelDataToAlphaMask } from '../PixelData/pixelDataToAlphaMask'
6
5
  * When possible use {@link pixelDataToAlphaMask} instead.
7
6
  * Repeat calls to the same data will use less memory.
8
7
  */
9
- export function imageDataToAlphaMask(
8
+ export function imageDataToAlphaMaskBuffer(
10
9
  imageData: ImageData,
11
- ): AlphaMask {
10
+ ): Uint8Array {
12
11
  const {
13
12
  width,
14
13
  height,
@@ -22,7 +21,7 @@ export function imageDataToAlphaMask(
22
21
  data.byteLength >> 2,
23
22
  )
24
23
  const len = data32.length
25
- const mask = new Uint8Array(width * height) as AlphaMask
24
+ const mask = new Uint8Array(width * height)
26
25
 
27
26
  for (let i = 0; i < len; i++) {
28
27
  const val = data32[i]
@@ -7,15 +7,13 @@ import type { ImageDataLike } from '../_types'
7
7
  * instead, it crops or pads the image based on the new dimensions and
8
8
  * provides an offset for repositioning.
9
9
  *
10
- * @param current The source {@link ImageDataLike} to resize.
10
+ * @param target The target to resize.
11
11
  * @param newWidth The target width in pixels.
12
12
  * @param newHeight The target height in pixels.
13
13
  * @param offsetX The horizontal offset for placing the
14
14
  * original image within the new buffer.
15
- * @default 0
16
15
  * @param offsetY The vertical offset for placing the
17
16
  * original image within the new buffer.
18
- * @default 0
19
17
  *
20
18
  * @returns A new {@link ImageData} instance with the specified dimensions.
21
19
  *
@@ -32,7 +30,7 @@ import type { ImageDataLike } from '../_types'
32
30
  * ```
33
31
  */
34
32
  export function resizeImageData(
35
- current: ImageDataLike,
33
+ target: ImageDataLike,
36
34
  newWidth: number,
37
35
  newHeight: number,
38
36
  offsetX = 0,
@@ -43,7 +41,7 @@ export function resizeImageData(
43
41
  width: oldW,
44
42
  height: oldH,
45
43
  data: oldData,
46
- } = current
44
+ } = target
47
45
  const newData = result.data
48
46
 
49
47
  // Determine intersection of the old image (at offset) and new canvas bounds
@@ -10,17 +10,17 @@ const SCRATCH_BLIT = makeClippedBlit()
10
10
  * into the target {@link ImageData} buffer. It supports both {@link Rect}
11
11
  * objects and discrete coordinates.
12
12
  *
13
- * @param imageData - The target {@link ImageData} to write into. Must match the rect width/height.
13
+ * @param target - The target to write into. Must match the rect width/height.
14
14
  * @param data - The source pixel data (RGBA).
15
- * @param rect - A {@link Rect} object defining the destination region.
15
+ * @param rect - A rect defining the destination region.
16
16
  */
17
17
  export function writeImageDataBuffer(
18
- imageData: ImageData,
18
+ target: ImageData,
19
19
  data: Uint8ClampedArray,
20
20
  rect: Rect,
21
21
  ): void
22
22
  /**
23
- * @param imageData - The target {@link ImageData} to write into.
23
+ * @param target - The target to write into.
24
24
  * @param data - The source pixel data (RGBA). Must match the width/height.
25
25
  * @param x - The starting horizontal coordinate in the target.
26
26
  * @param y - The starting vertical coordinate in the target.
@@ -28,7 +28,7 @@ export function writeImageDataBuffer(
28
28
  * @param h - The height of the region to write.
29
29
  */
30
30
  export function writeImageDataBuffer(
31
- imageData: ImageData,
31
+ target: ImageData,
32
32
  data: Uint8ClampedArray,
33
33
  x: number,
34
34
  y: number,
@@ -36,7 +36,7 @@ export function writeImageDataBuffer(
36
36
  h: number,
37
37
  ): void
38
38
  export function writeImageDataBuffer(
39
- imageData: ImageData,
39
+ target: ImageData,
40
40
  data: Uint8ClampedArray,
41
41
  _x: Rect | number,
42
42
  _y?: number,
@@ -47,7 +47,7 @@ export function writeImageDataBuffer(
47
47
  ? _x
48
48
  : { x: _x, y: _y!, w: _w!, h: _h! }
49
49
 
50
- const { width: dstW, height: dstH, data: dst } = imageData
50
+ const { width: dstW, height: dstH, data: dst } = target
51
51
 
52
52
  const clip = resolveBlitClipping(
53
53
  x,
@@ -0,0 +1,16 @@
1
+ import { type AlphaMask, MaskType } from '../_types'
2
+
3
+ /**
4
+ * Creates an Alpha Mask
5
+ * @param w - width
6
+ * @param h - height
7
+ * @param data - values 0-255
8
+ */
9
+ export function makeAlphaMask(w: number, h: number, data?: Uint8Array): AlphaMask {
10
+ return {
11
+ type: MaskType.ALPHA,
12
+ data: data ?? new Uint8Array(w * h),
13
+ w,
14
+ h,
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ import { type BinaryMask, MaskType } from '../_types'
2
+
3
+ /**
4
+ * Creates a Binary Mask
5
+ * @param w - width
6
+ * @param h - height
7
+ * @param data - values 0-1
8
+ */
9
+ export function makeBinaryMask(w: number, h: number, data?: Uint8Array): BinaryMask {
10
+ return {
11
+ type: MaskType.BINARY,
12
+ data: data ?? new Uint8Array(w * h),
13
+ w,
14
+ h,
15
+ }
16
+ }
@@ -0,0 +1,32 @@
1
+ import { type CircleBrushAlphaMask, MaskType } from '../_types'
2
+
3
+ export function makeCircleBrushAlphaMask(size: number, fallOff: (d: number) => number = () => 1): CircleBrushAlphaMask {
4
+ const area = size * size
5
+ const data = new Uint8Array(area)
6
+ const radius = size / 2
7
+ const invR = 1 / radius
8
+
9
+ const minOffset = -Math.ceil(radius - 0.5)
10
+
11
+ for (let y = 0; y < size; y++) {
12
+ for (let x = 0; x < size; x++) {
13
+ const dx = x - radius + 0.5
14
+ const dy = y - radius + 0.5
15
+ const distSqr = dx * dx + dy * dy
16
+ if (distSqr <= (radius * radius)) {
17
+ const dist = Math.sqrt(distSqr)
18
+ data[y * size + x] = (fallOff(1 - (dist * invR)) * 255) | 0
19
+ }
20
+ }
21
+ }
22
+
23
+ return {
24
+ type: MaskType.ALPHA,
25
+ data,
26
+ w: size,
27
+ h: size,
28
+ radius,
29
+ size,
30
+ minOffset,
31
+ }
32
+ }
@@ -0,0 +1,30 @@
1
+ import { type CircleBrushBinaryMask, MaskType } from '../_types'
2
+
3
+ export function makeCircleBrushBinaryMask(size: number): CircleBrushBinaryMask {
4
+ const area = size * size
5
+ const data = new Uint8Array(area)
6
+ const radius = size / 2
7
+
8
+ const minOffset = -Math.ceil(radius - 0.5)
9
+
10
+ for (let y = 0; y < size; y++) {
11
+ for (let x = 0; x < size; x++) {
12
+ const dx = x - radius + 0.5
13
+ const dy = y - radius + 0.5
14
+ const distSqr = dx * dx + dy * dy
15
+ if (distSqr <= (radius * radius)) {
16
+ data[y * size + x] = 1
17
+ }
18
+ }
19
+ }
20
+
21
+ return {
22
+ type: MaskType.BINARY,
23
+ data,
24
+ w: size,
25
+ h: size,
26
+ radius,
27
+ size,
28
+ minOffset,
29
+ }
30
+ }
@@ -2,9 +2,7 @@ import type { AlphaMask, ApplyMaskToPixelDataOptions, BinaryMask } from '../_typ
2
2
 
3
3
  export function applyBinaryMaskToAlphaMask(
4
4
  alphaMaskDst: AlphaMask,
5
- dstWidth: number,
6
5
  binaryMaskSrc: BinaryMask,
7
- srcWidth: number,
8
6
  opts: ApplyMaskToPixelDataOptions = {},
9
7
  ): void {
10
8
  const {
@@ -17,12 +15,14 @@ export function applyBinaryMaskToAlphaMask(
17
15
  invertMask = false,
18
16
  } = opts
19
17
 
18
+ const dstWidth = alphaMaskDst.w
20
19
  if (dstWidth <= 0) return
21
- if (binaryMaskSrc.length === 0) return
20
+ if (binaryMaskSrc.data.length === 0) return
21
+ const srcWidth = binaryMaskSrc.w
22
22
  if (srcWidth <= 0) return
23
23
 
24
- const dstHeight = (alphaMaskDst.length / dstWidth) | 0
25
- const srcHeight = (binaryMaskSrc.length / srcWidth) | 0
24
+ const dstHeight = (alphaMaskDst.data.length / dstWidth) | 0
25
+ const srcHeight = (binaryMaskSrc.data.length / srcWidth) | 0
26
26
 
27
27
  if (dstHeight <= 0) return
28
28
  if (srcHeight <= 0) return
@@ -46,6 +46,9 @@ export function applyBinaryMaskToAlphaMask(
46
46
  const iterW = Math.min(dstX1 - dstX0, srcWidth - srcX0)
47
47
  const iterH = Math.min(dstY1 - dstY0, srcHeight - srcY0)
48
48
 
49
+ const srcData = binaryMaskSrc.data
50
+ const dstData = alphaMaskDst.data
51
+
49
52
  let dstIdx = dstY0 * dstWidth + dstX0
50
53
  let srcIdx = srcY0 * srcWidth + srcX0
51
54
 
@@ -57,8 +60,8 @@ export function applyBinaryMaskToAlphaMask(
57
60
 
58
61
  while (d < dstEnd) {
59
62
  // inverted
60
- if (binaryMaskSrc[s] !== 0) {
61
- alphaMaskDst[d] = 0
63
+ if (srcData[s] !== 0) {
64
+ dstData[d] = 0
62
65
  }
63
66
  d++
64
67
  s++
@@ -75,8 +78,8 @@ export function applyBinaryMaskToAlphaMask(
75
78
 
76
79
  while (d < dstEnd) {
77
80
  // If binary mask is empty, clear the alpha pixel.
78
- if (binaryMaskSrc[s] === 0) {
79
- alphaMaskDst[d] = 0
81
+ if (srcData[s] === 0) {
82
+ dstData[d] = 0
80
83
  }
81
84
  d++
82
85
  s++
@@ -1,8 +1,14 @@
1
+ import { type Mask } from '../_types'
2
+
1
3
  /**
2
4
  * Creates a new copy of a mask.
3
5
  * Uses the underlying buffer's slice method for high-performance memory copying.
4
6
  */
5
- export function copyMask<T extends Uint8Array>(src: T): T {
6
- // Uint8Array.slice() is highly optimized at the engine level
7
- return src.slice() as T
7
+ export function copyMask<T extends Mask>(src: T): T {
8
+ return {
9
+ type: src.type,
10
+ data: src.data.slice(),
11
+ w: src.w,
12
+ h: src.h,
13
+ } as T
8
14
  }
@@ -1,45 +1,41 @@
1
- import type { Rect } from '../_types'
1
+ import { type Mask, type Rect } from '../_types'
2
2
 
3
3
  /**
4
4
  * Extracts a rectangular region from a 1D {@link Uint8Array} mask.
5
5
  * This utility calculates the necessary offsets based on the `maskWidth` to
6
6
  * slice out a specific area.
7
7
  *
8
- * @param mask - The source 1D array representing the full 2D mask.
9
- * @param maskWidth - The width of the original source mask (stride).
10
- * @param rect - A {@link Rect} object defining the region to extract.
11
- * @returns A new {@link Uint8Array} containing the extracted region.
8
+ * @param mask - The target mask.
9
+ * @param rect - A rect defining the region to extract.
10
+ * @returns A new mask containing the extracted region.
12
11
  */
13
- export function extractMask(
14
- mask: Uint8Array,
15
- maskWidth: number,
12
+ export function extractMask<T extends Mask>(
13
+ mask: T,
16
14
  rect: Rect,
17
- ): Uint8Array
15
+ ): T
16
+
18
17
  /**
19
- * @param mask - The source 1D array representing the full 2D mask.
20
- * @param maskWidth - The width of the original source mask (stride).
18
+ * @param mask - The target mask.
21
19
  * @param x - The starting horizontal coordinate.
22
20
  * @param y - The starting vertical coordinate.
23
21
  * @param w - The width of the region to extract.
24
22
  * @param h - The height of the region to extract.
25
23
  * @returns A new {@link Uint8Array} containing the extracted region.
26
24
  */
27
- export function extractMask(
28
- mask: Uint8Array,
29
- maskWidth: number,
25
+ export function extractMask<T extends Mask>(
26
+ mask: T,
30
27
  x: number,
31
28
  y: number,
32
29
  w: number,
33
30
  h: number,
34
- ): Uint8Array
35
- export function extractMask(
36
- mask: Uint8Array,
37
- maskWidth: number,
31
+ ): T
32
+ export function extractMask<T extends Mask>(
33
+ mask: T,
38
34
  xOrRect: number | Rect,
39
35
  y?: number,
40
36
  w?: number,
41
37
  h?: number,
42
- ): Uint8Array {
38
+ ): T {
43
39
  let finalX: number
44
40
  let finalY: number
45
41
  let finalW: number
@@ -57,28 +53,34 @@ export function extractMask(
57
53
  finalH = h!
58
54
  }
59
55
 
60
- const out = new Uint8Array(finalW * finalH)
61
- const srcH = mask.length / maskWidth
56
+ const out = {
57
+ type: mask.type,
58
+ w: finalW,
59
+ h: finalH,
60
+ data: new Uint8Array(finalW * finalH),
61
+ } as T
62
+
63
+ // Calculate the total height of the source mask based on the buffer size
64
+ const srcH = mask.h
65
+ const stride = mask.w
62
66
 
63
67
  for (let row = 0; row < finalH; row++) {
64
68
  const currentSrcY = finalY + row
65
69
 
66
- if (currentSrcY < 0 || currentSrcY >= srcH) {
67
- continue
68
- }
70
+ // Safety Check: If the requested row is outside the source mask, skip it (leave as 0)
71
+ if (currentSrcY < 0 || currentSrcY >= srcH) continue
69
72
 
73
+ // Calculate valid horizontal range within the source stride
74
+ // We only copy if srcX is within the actual bounds of the source width
70
75
  const start = Math.max(0, finalX)
71
- const end = Math.min(maskWidth, finalX + finalW)
76
+ const end = Math.min(stride, finalX + finalW)
72
77
 
73
78
  if (start < end) {
74
- const srcOffset = currentSrcY * maskWidth + start
75
- const dstOffset = (row * finalW) + (start - finalX)
79
+ const srcOffset = currentSrcY * stride + start
80
+ const dstOffset = row * finalW + (start - finalX)
76
81
  const count = end - start
77
82
 
78
- out.set(
79
- mask.subarray(srcOffset, srcOffset + count),
80
- dstOffset,
81
- )
83
+ out.data.set(mask.data.subarray(srcOffset, srcOffset + count), dstOffset)
82
84
  }
83
85
  }
84
86