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.
- package/dist/index.dev.cjs +722 -357
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +709 -356
- package/dist/index.dev.js.map +1 -1
- package/dist/index.prod.cjs +722 -357
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +283 -128
- package/dist/index.prod.js +709 -356
- package/dist/index.prod.js.map +1 -1
- package/package.json +1 -1
- package/src/Algorithm/floodFillSelection.ts +12 -14
- package/src/BlendModes/toBlendModeIndexAndName.ts +0 -7
- package/src/Clipboard/writeImgBlobToClipboard.ts +1 -1
- package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +3 -0
- package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +3 -0
- package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +25 -6
- package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +89 -46
- package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +7 -7
- package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +81 -41
- package/src/History/PixelMutator/mutatorApplyRectBrush.ts +3 -0
- package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +18 -5
- package/src/History/PixelMutator/mutatorApplyRectPencil.ts +3 -0
- package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +19 -4
- package/src/History/PixelMutator/mutatorBlendColor.ts +4 -0
- package/src/History/PixelMutator/mutatorBlendPixelData.ts +4 -0
- package/src/History/PixelMutator/mutatorClear.ts +11 -8
- package/src/History/PixelMutator/mutatorFill.ts +4 -0
- package/src/History/PixelMutator/mutatorFillBinaryMask.ts +28 -0
- package/src/History/PixelMutator/mutatorInvert.ts +3 -0
- package/src/ImageData/extractImageDataBuffer.ts +3 -3
- package/src/ImageData/{imageDataToAlphaMask.ts → imageDataToAlphaMaskBuffer.ts} +3 -4
- package/src/ImageData/resizeImageData.ts +3 -5
- package/src/ImageData/writeImageDataBuffer.ts +7 -7
- package/src/Mask/AlphaMask.ts +16 -0
- package/src/Mask/BinaryMask.ts +16 -0
- package/src/Mask/CircleBrushAlphaMask.ts +32 -0
- package/src/Mask/CircleBrushBinaryMask.ts +30 -0
- package/src/Mask/applyBinaryMaskToAlphaMask.ts +12 -9
- package/src/Mask/copyMask.ts +9 -3
- package/src/Mask/extractMask.ts +33 -31
- package/src/Mask/extractMaskBuffer.ts +87 -0
- package/src/Mask/invertMask.ts +6 -4
- package/src/Mask/mergeAlphaMasks.ts +11 -10
- package/src/Mask/mergeBinaryMasks.ts +10 -9
- package/src/Mask/setMaskData.ts +7 -0
- package/src/MaskRect/merge2BinaryMaskRects.ts +81 -0
- package/src/MaskRect/mergeBinaryMaskRects.ts +39 -0
- package/src/MaskRect/subtractBinaryMaskRects.ts +80 -0
- package/src/PixelData/applyAlphaMaskToPixelData.ts +8 -5
- package/src/PixelData/applyBinaryMaskToPixelData.ts +8 -9
- package/src/PixelData/applyCircleBrushToPixelData.ts +54 -62
- package/src/PixelData/blendColorPixelDataAlphaMask.ts +35 -25
- package/src/PixelData/blendColorPixelDataBinaryMask.ts +26 -19
- package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
- package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
- package/src/PixelData/clearPixelData.ts +2 -2
- package/src/PixelData/fillPixelData.ts +15 -42
- package/src/PixelData/fillPixelDataBinaryMask.ts +79 -0
- package/src/PixelData/invertPixelData.ts +3 -3
- package/src/PixelData/pixelDataToAlphaMask.ts +4 -2
- package/src/PixelData/writePixelDataBuffer.ts +2 -3
- package/src/Rect/getRectsBounds.ts +22 -0
- package/src/Rect/trimRectBounds.ts +20 -17
- package/src/_types.ts +55 -29
- 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
|
-
|
|
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 >
|
|
147
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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<
|
|
19
|
+
rect: Partial<BinaryMaskRect> = {},
|
|
16
20
|
) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
14
|
-
* @returns A
|
|
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
|
|
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
|
|
8
|
+
export function imageDataToAlphaMaskBuffer(
|
|
10
9
|
imageData: ImageData,
|
|
11
|
-
):
|
|
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)
|
|
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
|
|
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
|
-
|
|
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
|
-
} =
|
|
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
|
|
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
|
|
15
|
+
* @param rect - A rect defining the destination region.
|
|
16
16
|
*/
|
|
17
17
|
export function writeImageDataBuffer(
|
|
18
|
-
|
|
18
|
+
target: ImageData,
|
|
19
19
|
data: Uint8ClampedArray,
|
|
20
20
|
rect: Rect,
|
|
21
21
|
): void
|
|
22
22
|
/**
|
|
23
|
-
* @param
|
|
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
|
-
|
|
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
|
-
|
|
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 } =
|
|
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 (
|
|
61
|
-
|
|
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 (
|
|
79
|
-
|
|
81
|
+
if (srcData[s] === 0) {
|
|
82
|
+
dstData[d] = 0
|
|
80
83
|
}
|
|
81
84
|
d++
|
|
82
85
|
s++
|
package/src/Mask/copyMask.ts
CHANGED
|
@@ -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
|
|
6
|
-
|
|
7
|
-
|
|
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
|
}
|
package/src/Mask/extractMask.ts
CHANGED
|
@@ -1,45 +1,41 @@
|
|
|
1
|
-
import type
|
|
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
|
|
9
|
-
* @param
|
|
10
|
-
* @
|
|
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:
|
|
15
|
-
maskWidth: number,
|
|
12
|
+
export function extractMask<T extends Mask>(
|
|
13
|
+
mask: T,
|
|
16
14
|
rect: Rect,
|
|
17
|
-
):
|
|
15
|
+
): T
|
|
16
|
+
|
|
18
17
|
/**
|
|
19
|
-
* @param mask - The
|
|
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:
|
|
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
|
-
):
|
|
35
|
-
export function extractMask(
|
|
36
|
-
mask:
|
|
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
|
-
):
|
|
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 =
|
|
61
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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(
|
|
76
|
+
const end = Math.min(stride, finalX + finalW)
|
|
72
77
|
|
|
73
78
|
if (start < end) {
|
|
74
|
-
const srcOffset = currentSrcY *
|
|
75
|
-
const dstOffset =
|
|
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
|
|