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
|
@@ -1,99 +1,91 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
type BlendColor32,
|
|
3
|
+
type CircleBrushMask,
|
|
4
|
+
type Color32,
|
|
5
|
+
type ColorBlendMaskOptions,
|
|
6
|
+
type IPixelData,
|
|
7
|
+
MaskType,
|
|
8
|
+
type Rect,
|
|
9
|
+
} from '../_types'
|
|
2
10
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
11
|
import { getCircleBrushOrPencilBounds } from '../Rect/getCircleBrushOrPencilBounds'
|
|
12
|
+
import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
|
|
13
|
+
import { blendColorPixelDataBinaryMask } from './blendColorPixelDataBinaryMask'
|
|
4
14
|
|
|
5
15
|
/**
|
|
6
|
-
* Applies a circular brush to pixel data
|
|
16
|
+
* Applies a circular brush to pixel data using a pre-calculated alpha mask.
|
|
7
17
|
*
|
|
8
18
|
* @param target The PixelData to modify.
|
|
9
19
|
* @param color The brush color.
|
|
10
20
|
* @param centerX The center x-coordinate of the brush.
|
|
11
21
|
* @param centerY The center y-coordinate of the brush.
|
|
12
|
-
* @param
|
|
22
|
+
* @param brush The pre-calculated CircleBrushAlphaMask.
|
|
13
23
|
* @param alpha The overall opacity of the brush (0-255).
|
|
14
|
-
* @default 255
|
|
15
|
-
* @param fallOff A function that returns an alpha multiplier (0-1) based on the normalized distance (0-1) from the circle's center.
|
|
16
24
|
* @param blendFn
|
|
25
|
+
* @param scratchOptions
|
|
17
26
|
* @param bounds precalculated result from {@link getCircleBrushOrPencilBounds}
|
|
18
|
-
* @default sourceOverPerfect
|
|
19
27
|
*/
|
|
20
28
|
export function applyCircleBrushToPixelData(
|
|
21
29
|
target: IPixelData,
|
|
22
30
|
color: Color32,
|
|
23
31
|
centerX: number,
|
|
24
32
|
centerY: number,
|
|
25
|
-
|
|
33
|
+
brush: CircleBrushMask,
|
|
26
34
|
alpha = 255,
|
|
27
|
-
fallOff: (dist: number) => number,
|
|
28
35
|
blendFn: BlendColor32 = sourceOverPerfect,
|
|
36
|
+
scratchOptions: ColorBlendMaskOptions = {},
|
|
29
37
|
bounds?: Rect,
|
|
30
38
|
): void {
|
|
31
|
-
const targetWidth = target.width
|
|
32
|
-
const targetHeight = target.height
|
|
33
|
-
|
|
34
|
-
// Use provided bounds OR calculate them once
|
|
35
39
|
const b = bounds ?? getCircleBrushOrPencilBounds(
|
|
36
40
|
centerX,
|
|
37
41
|
centerY,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
brush.size,
|
|
43
|
+
target.width,
|
|
44
|
+
target.height,
|
|
41
45
|
)
|
|
42
46
|
|
|
43
47
|
if (b.w <= 0 || b.h <= 0) return
|
|
44
48
|
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const rSqr = r * r
|
|
48
|
-
const invR = 1 / r
|
|
49
|
-
|
|
50
|
-
const centerOffset = (brushSize % 2 === 0) ? 0.5 : 0
|
|
51
|
-
|
|
52
|
-
const endX = b.x + b.w
|
|
53
|
-
const endY = b.y + b.h
|
|
49
|
+
const unclippedStartX = Math.floor(centerX + brush.minOffset)
|
|
50
|
+
const unclippedStartY = Math.floor(centerY + brush.minOffset)
|
|
54
51
|
|
|
55
|
-
//
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const isOpaque = alpha === 255
|
|
61
|
-
const isOverwrite = (blendFn as any).isOverwrite
|
|
52
|
+
// Calculate the intersection between the unclipped mask rect and the allowed bounds
|
|
53
|
+
const ix = Math.max(unclippedStartX, b.x)
|
|
54
|
+
const iy = Math.max(unclippedStartY, b.y)
|
|
55
|
+
const ir = Math.min(unclippedStartX + brush.w, b.x + b.w)
|
|
56
|
+
const ib = Math.min(unclippedStartY + brush.h, b.y + b.h)
|
|
62
57
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const dySqr = relY * relY
|
|
66
|
-
const rowOffset = cy * targetWidth
|
|
58
|
+
const iw = ir - ix
|
|
59
|
+
const ih = ib - iy
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const dSqr = relX * relX + dySqr
|
|
61
|
+
// If the mask falls entirely outside the bounds, exit
|
|
62
|
+
if (iw <= 0 || ih <= 0) return
|
|
71
63
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
// Apply the intersected coordinates and internal mask offsets
|
|
65
|
+
scratchOptions.x = ix
|
|
66
|
+
scratchOptions.y = iy
|
|
67
|
+
scratchOptions.w = iw
|
|
68
|
+
scratchOptions.h = ih
|
|
69
|
+
scratchOptions.mx = ix - unclippedStartX
|
|
70
|
+
scratchOptions.my = iy - unclippedStartY
|
|
71
|
+
scratchOptions.alpha = alpha
|
|
72
|
+
scratchOptions.blendFn = blendFn
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
weight = (maskVal * alpha + 128) >> 8
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Match Blitter's final color calculation exactly
|
|
88
|
-
let finalCol = color
|
|
89
|
-
if (weight < 255) {
|
|
90
|
-
const a = (baseSrcAlpha * weight + 128) >> 8
|
|
91
|
-
if (a === 0 && !isOverwrite) continue
|
|
92
|
-
finalCol = (colorRGB | (a << 24)) >>> 0 as Color32
|
|
93
|
-
}
|
|
74
|
+
if (brush.type === MaskType.ALPHA) {
|
|
75
|
+
blendColorPixelDataAlphaMask(
|
|
76
|
+
target,
|
|
77
|
+
color,
|
|
78
|
+
brush,
|
|
79
|
+
scratchOptions,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
94
82
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
if (brush.type === MaskType.BINARY) {
|
|
84
|
+
blendColorPixelDataBinaryMask(
|
|
85
|
+
target,
|
|
86
|
+
color,
|
|
87
|
+
brush,
|
|
88
|
+
scratchOptions,
|
|
89
|
+
)
|
|
98
90
|
}
|
|
99
91
|
}
|
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
import type { AlphaMask, Color32, ColorBlendMaskOptions, IPixelData } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Blends a solid color into a target pixel buffer using an alpha mask.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* If the width (`w`) or height (`h`) are omitted from the options, they will safely
|
|
9
|
+
* default to the dimensions of the provided mask to prevent out-of-bounds memory access.
|
|
10
|
+
*
|
|
11
|
+
* @param dst - The destination {@link IPixelData} buffer to modify.
|
|
12
|
+
* @param color - The solid color to apply.
|
|
13
|
+
* @param mask - The mask defining the per-pixel opacity of the target area.
|
|
14
|
+
* @param opts - Configuration options including placement coordinates, bounds, global alpha, and mask offsets.
|
|
15
|
+
*/
|
|
4
16
|
export function blendColorPixelDataAlphaMask(
|
|
5
17
|
dst: IPixelData,
|
|
6
18
|
color: Color32,
|
|
7
19
|
mask: AlphaMask,
|
|
8
|
-
opts: ColorBlendMaskOptions,
|
|
9
|
-
) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} = opts
|
|
22
|
-
|
|
23
|
-
if (globalAlpha === 0 || !mask) return
|
|
20
|
+
opts: ColorBlendMaskOptions = {},
|
|
21
|
+
): void {
|
|
22
|
+
const targetX = opts.x ?? 0
|
|
23
|
+
const targetY = opts.y ?? 0
|
|
24
|
+
const w = opts.w ?? mask.w
|
|
25
|
+
const h = opts.h ?? mask.h
|
|
26
|
+
const globalAlpha = opts.alpha ?? 255
|
|
27
|
+
const blendFn = opts.blendFn ?? sourceOverPerfect
|
|
28
|
+
const mx = opts.mx ?? 0
|
|
29
|
+
const my = opts.my ?? 0
|
|
30
|
+
const invertMask = opts.invertMask ?? false
|
|
31
|
+
|
|
32
|
+
if (globalAlpha === 0) return
|
|
24
33
|
|
|
25
34
|
const baseSrcAlpha = (color >>> 24)
|
|
26
35
|
const isOverwrite = (blendFn as any).isOverwrite || false
|
|
@@ -29,21 +38,21 @@ export function blendColorPixelDataAlphaMask(
|
|
|
29
38
|
|
|
30
39
|
let x = targetX
|
|
31
40
|
let y = targetY
|
|
32
|
-
let
|
|
33
|
-
let
|
|
41
|
+
let actualW = w
|
|
42
|
+
let actualH = h
|
|
34
43
|
|
|
35
44
|
if (x < 0) {
|
|
36
|
-
|
|
45
|
+
actualW += x
|
|
37
46
|
x = 0
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
if (y < 0) {
|
|
41
|
-
|
|
50
|
+
actualH += y
|
|
42
51
|
y = 0
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
actualW = Math.min(actualW, dst.width - x)
|
|
55
|
+
actualH = Math.min(actualH, dst.height - y)
|
|
47
56
|
|
|
48
57
|
if (actualW <= 0 || actualH <= 0) return
|
|
49
58
|
|
|
@@ -52,7 +61,8 @@ export function blendColorPixelDataAlphaMask(
|
|
|
52
61
|
|
|
53
62
|
const dst32 = dst.data32
|
|
54
63
|
const dw = dst.width
|
|
55
|
-
const mPitch =
|
|
64
|
+
const mPitch = mask.w
|
|
65
|
+
const maskData = mask.data
|
|
56
66
|
|
|
57
67
|
let dIdx = (y * dw + x) | 0
|
|
58
68
|
let mIdx = ((my + dy) * mPitch + (mx + dx)) | 0
|
|
@@ -64,7 +74,7 @@ export function blendColorPixelDataAlphaMask(
|
|
|
64
74
|
|
|
65
75
|
for (let iy = 0; iy < actualH; iy++) {
|
|
66
76
|
for (let ix = 0; ix < actualW; ix++) {
|
|
67
|
-
const mVal =
|
|
77
|
+
const mVal = maskData[mIdx]
|
|
68
78
|
const effM = invertMask ? 255 - mVal : mVal
|
|
69
79
|
|
|
70
80
|
if (effM === 0) {
|
|
@@ -96,7 +106,7 @@ export function blendColorPixelDataAlphaMask(
|
|
|
96
106
|
mIdx++
|
|
97
107
|
continue
|
|
98
108
|
}
|
|
99
|
-
finalCol = (colorRGB | (a << 24)) >>> 0 as Color32
|
|
109
|
+
finalCol = ((colorRGB | (a << 24)) >>> 0) as Color32
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
dst32[dIdx] = blendFn(finalCol, dst32[dIdx] as Color32)
|
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
import type { BinaryMask, Color32, ColorBlendMaskOptions, IPixelData } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Blends a solid color into a target pixel buffer using a binary mask.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* If the width (`w`) or height (`h`) are omitted from the options, they will safely
|
|
9
|
+
* default to the dimensions of the provided mask to prevent out-of-bounds memory access.
|
|
10
|
+
*
|
|
11
|
+
* @param dst - The destination {@link IPixelData} buffer to modify.
|
|
12
|
+
* @param color - The solid color to apply.
|
|
13
|
+
* @param mask - The mask defining the per-pixel opacity of the target area.
|
|
14
|
+
* @param opts - Configuration options including placement coordinates, bounds, global alpha, and mask offsets.
|
|
15
|
+
*/
|
|
4
16
|
export function blendColorPixelDataBinaryMask(
|
|
5
17
|
dst: IPixelData,
|
|
6
18
|
color: Color32,
|
|
7
19
|
mask: BinaryMask,
|
|
8
20
|
opts: ColorBlendMaskOptions = {},
|
|
9
21
|
) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} = opts
|
|
22
|
-
|
|
23
|
-
if (globalAlpha === 0 || !mask) return
|
|
22
|
+
const targetX = opts.x ?? 0
|
|
23
|
+
const targetY = opts.y ?? 0
|
|
24
|
+
let w = opts.w ?? mask.w
|
|
25
|
+
let h = opts.h ?? mask.h
|
|
26
|
+
const globalAlpha = opts.alpha ?? 255
|
|
27
|
+
const blendFn = opts.blendFn ?? sourceOverPerfect
|
|
28
|
+
const mx = opts.mx ?? 0
|
|
29
|
+
const my = opts.my ?? 0
|
|
30
|
+
const invertMask = opts.invertMask ?? false
|
|
31
|
+
|
|
32
|
+
if (globalAlpha === 0) return
|
|
24
33
|
|
|
25
34
|
const baseSrcAlpha = (color >>> 24)
|
|
26
35
|
const isOverwrite = (blendFn as any).isOverwrite || false
|
|
@@ -29,8 +38,6 @@ export function blendColorPixelDataBinaryMask(
|
|
|
29
38
|
|
|
30
39
|
let x = targetX
|
|
31
40
|
let y = targetY
|
|
32
|
-
let w = width
|
|
33
|
-
let h = height
|
|
34
41
|
|
|
35
42
|
if (x < 0) {
|
|
36
43
|
w += x
|
|
@@ -60,8 +67,8 @@ export function blendColorPixelDataBinaryMask(
|
|
|
60
67
|
|
|
61
68
|
const dst32 = dst.data32
|
|
62
69
|
const dw = dst.width
|
|
63
|
-
const mPitch =
|
|
64
|
-
|
|
70
|
+
const mPitch = mask.w
|
|
71
|
+
const maskData = mask.data
|
|
65
72
|
let dIdx = (y * dw + x) | 0
|
|
66
73
|
let mIdx = ((my + dy) * mPitch + (mx + dx)) | 0
|
|
67
74
|
|
|
@@ -71,7 +78,7 @@ export function blendColorPixelDataBinaryMask(
|
|
|
71
78
|
|
|
72
79
|
for (let iy = 0; iy < actualH; iy++) {
|
|
73
80
|
for (let ix = 0; ix < actualW; ix++) {
|
|
74
|
-
if (
|
|
81
|
+
if (maskData[mIdx] === skipVal) {
|
|
75
82
|
dIdx++
|
|
76
83
|
mIdx++
|
|
77
84
|
continue
|
|
@@ -16,7 +16,6 @@ export function blendPixelDataAlphaMask(
|
|
|
16
16
|
h: height = src.height,
|
|
17
17
|
alpha: globalAlpha = 255,
|
|
18
18
|
blendFn = sourceOverPerfect,
|
|
19
|
-
mw = src.width,
|
|
20
19
|
mx = 0,
|
|
21
20
|
my = 0,
|
|
22
21
|
invertMask = false,
|
|
@@ -62,7 +61,8 @@ export function blendPixelDataAlphaMask(
|
|
|
62
61
|
// 2. Index Setup
|
|
63
62
|
const dw = dst.width
|
|
64
63
|
const sw = src.width
|
|
65
|
-
const mPitch =
|
|
64
|
+
const mPitch = alphaMask.w
|
|
65
|
+
const maskData = alphaMask.data
|
|
66
66
|
|
|
67
67
|
// dx/dy is the displacement from requested start to clipped start.
|
|
68
68
|
// This keeps the mask locked to the source content during cross-clipping.
|
|
@@ -85,7 +85,7 @@ export function blendPixelDataAlphaMask(
|
|
|
85
85
|
|
|
86
86
|
for (let iy = 0; iy < actualH; iy++) {
|
|
87
87
|
for (let ix = 0; ix < actualW; ix++) {
|
|
88
|
-
const mVal =
|
|
88
|
+
const mVal = maskData[mIdx]
|
|
89
89
|
const effM = invertMask ? 255 - mVal : mVal
|
|
90
90
|
|
|
91
91
|
// Early exit if mask is fully transparent
|
|
@@ -16,7 +16,6 @@ export function blendPixelDataBinaryMask(
|
|
|
16
16
|
h: height = src.height,
|
|
17
17
|
alpha: globalAlpha = 255,
|
|
18
18
|
blendFn = sourceOverPerfect,
|
|
19
|
-
mw = src.width,
|
|
20
19
|
mx = 0,
|
|
21
20
|
my = 0,
|
|
22
21
|
invertMask = false,
|
|
@@ -72,7 +71,8 @@ export function blendPixelDataBinaryMask(
|
|
|
72
71
|
const src32 = src.data32
|
|
73
72
|
const dw = dst.width
|
|
74
73
|
const sw = src.width
|
|
75
|
-
const mPitch =
|
|
74
|
+
const mPitch = binaryMask.w
|
|
75
|
+
const maskData = binaryMask.data
|
|
76
76
|
|
|
77
77
|
let dIdx = (y * dw + x) | 0
|
|
78
78
|
let sIdx = (sy * sw + sx) | 0
|
|
@@ -89,7 +89,7 @@ export function blendPixelDataBinaryMask(
|
|
|
89
89
|
for (let iy = 0; iy < actualH; iy++) {
|
|
90
90
|
for (let ix = 0; ix < actualW; ix++) {
|
|
91
91
|
// Binary Mask Check (Earliest exit)
|
|
92
|
-
if (
|
|
92
|
+
if (maskData[mIdx] === skipVal) {
|
|
93
93
|
dIdx++
|
|
94
94
|
sIdx++
|
|
95
95
|
mIdx++
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Color32, IPixelData
|
|
1
|
+
import type { BinaryMaskRect, Color32, IPixelData } from '../_types'
|
|
2
2
|
import { fillPixelData } from './fillPixelData'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,7 +7,7 @@ import { fillPixelData } from './fillPixelData'
|
|
|
7
7
|
*/
|
|
8
8
|
export function clearPixelData(
|
|
9
9
|
dst: IPixelData,
|
|
10
|
-
rect?: Partial<
|
|
10
|
+
rect?: Partial<BinaryMaskRect>,
|
|
11
11
|
): void {
|
|
12
12
|
fillPixelData(dst, 0 as Color32, rect)
|
|
13
13
|
}
|
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Color32, IPixelData, Rect } from '../_types'
|
|
2
2
|
import { makeClippedRect, resolveRectClipping } from '../Internal/resolveClipping'
|
|
3
|
-
import type { PixelData } from './PixelData'
|
|
4
3
|
|
|
5
4
|
const SCRATCH_RECT = makeClippedRect()
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
* Fills a region or the {@link
|
|
7
|
+
* Fills a region or the {@link IPixelData} buffer with a solid color.
|
|
9
8
|
*
|
|
10
|
-
* @param dst - The target
|
|
11
|
-
* @param color - The
|
|
12
|
-
* @param rect -
|
|
9
|
+
* @param dst - The target to modify.
|
|
10
|
+
* @param color - The color to apply.
|
|
11
|
+
* @param rect - Defines the area to fill. If omitted, the entire
|
|
12
|
+
* buffer is filled.
|
|
13
13
|
*/
|
|
14
14
|
export function fillPixelData(
|
|
15
15
|
dst: IPixelData,
|
|
16
16
|
color: Color32,
|
|
17
|
-
rect?: Partial<
|
|
17
|
+
rect?: Partial<Rect>,
|
|
18
18
|
): void
|
|
19
19
|
/**
|
|
20
|
-
* @param dst - The target
|
|
21
|
-
* @param color - The
|
|
20
|
+
* @param dst - The target to modify.
|
|
21
|
+
* @param color - The color to apply.
|
|
22
22
|
* @param x - Starting horizontal coordinate.
|
|
23
23
|
* @param y - Starting vertical coordinate.
|
|
24
24
|
* @param w - Width of the fill area.
|
|
25
25
|
* @param h - Height of the fill area.
|
|
26
|
-
* @param mask - A {@link BinaryMaskRect} defining the area to fill
|
|
27
26
|
*/
|
|
28
27
|
export function fillPixelData(
|
|
29
28
|
dst: IPixelData,
|
|
@@ -32,36 +31,30 @@ export function fillPixelData(
|
|
|
32
31
|
y: number,
|
|
33
32
|
w: number,
|
|
34
33
|
h: number,
|
|
35
|
-
mask?: BinaryMask,
|
|
36
34
|
): void
|
|
37
35
|
export function fillPixelData(
|
|
38
36
|
dst: IPixelData,
|
|
39
37
|
color: Color32,
|
|
40
|
-
_x?: Partial<
|
|
38
|
+
_x?: Partial<Rect> | number,
|
|
41
39
|
_y?: number,
|
|
42
40
|
_w?: number,
|
|
43
41
|
_h?: number,
|
|
44
|
-
_mask?: BinaryMask,
|
|
45
42
|
): void {
|
|
46
43
|
let x: number
|
|
47
44
|
let y: number
|
|
48
45
|
let w: number
|
|
49
46
|
let h: number
|
|
50
|
-
let mask: BinaryMask | undefined
|
|
51
47
|
|
|
52
48
|
if (typeof _x === 'object') {
|
|
53
49
|
x = _x.x ?? 0
|
|
54
50
|
y = _x.y ?? 0
|
|
55
51
|
w = _x.w ?? dst.width
|
|
56
52
|
h = _x.h ?? dst.height
|
|
57
|
-
mask = _x.mask
|
|
58
|
-
|
|
59
53
|
} else if (typeof _x === 'number') {
|
|
60
54
|
x = _x
|
|
61
55
|
y = _y!
|
|
62
56
|
w = _w!
|
|
63
57
|
h = _h!
|
|
64
|
-
mask = _mask
|
|
65
58
|
} else {
|
|
66
59
|
x = 0
|
|
67
60
|
y = 0
|
|
@@ -90,30 +83,10 @@ export function fillPixelData(
|
|
|
90
83
|
return
|
|
91
84
|
}
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
for (let ix = 0; ix < actualW; ix++) {
|
|
100
|
-
const currentX = finalX + ix
|
|
101
|
-
const maskX = currentX - x
|
|
102
|
-
const maskIndex = maskOffset + maskX
|
|
103
|
-
const isMasked = mask[maskIndex]
|
|
104
|
-
|
|
105
|
-
if (isMasked) {
|
|
106
|
-
const dstIndex = currentY * dw + currentX
|
|
107
|
-
dst32[dstIndex] = color
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
// Row-by-row fill for partial rectangles
|
|
113
|
-
for (let iy = 0; iy < actualH; iy++) {
|
|
114
|
-
const start = (finalY + iy) * dw + finalX
|
|
115
|
-
const end = start + actualW
|
|
116
|
-
dst32.fill(color, start, end)
|
|
117
|
-
}
|
|
86
|
+
// Row-by-row fill for partial rectangles
|
|
87
|
+
for (let iy = 0; iy < actualH; iy++) {
|
|
88
|
+
const start = (finalY + iy) * dw + finalX
|
|
89
|
+
const end = start + actualW
|
|
90
|
+
dst32.fill(color, start, end)
|
|
118
91
|
}
|
|
119
92
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { BinaryMask, Color32, IPixelData } from '../_types'
|
|
2
|
+
import { makeClippedRect, resolveRectClipping } from '../Internal/resolveClipping'
|
|
3
|
+
|
|
4
|
+
const SCRATCH_RECT = makeClippedRect()
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fills a region of the {@link IPixelData} buffer with a solid color using a mask.
|
|
8
|
+
* @param dst - The target to modify.
|
|
9
|
+
* @param color - The color to apply.
|
|
10
|
+
* @param mask - The mask defining the area to fill.
|
|
11
|
+
* @param alpha - The overall opacity of the fill (0-255).
|
|
12
|
+
* @param x - Starting horizontal coordinate for the mask placement.
|
|
13
|
+
* @param y - Starting vertical coordinate for the mask placement.
|
|
14
|
+
*/
|
|
15
|
+
export function fillPixelDataBinaryMask(
|
|
16
|
+
dst: IPixelData,
|
|
17
|
+
color: Color32,
|
|
18
|
+
mask: BinaryMask,
|
|
19
|
+
alpha = 255,
|
|
20
|
+
x = 0,
|
|
21
|
+
y = 0,
|
|
22
|
+
): void {
|
|
23
|
+
if (alpha === 0) return
|
|
24
|
+
|
|
25
|
+
const maskW = mask.w
|
|
26
|
+
const maskH = mask.h
|
|
27
|
+
|
|
28
|
+
const clip = resolveRectClipping(
|
|
29
|
+
x,
|
|
30
|
+
y,
|
|
31
|
+
maskW,
|
|
32
|
+
maskH,
|
|
33
|
+
dst.width,
|
|
34
|
+
dst.height,
|
|
35
|
+
SCRATCH_RECT,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if (!clip.inBounds) return
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
x: finalX,
|
|
42
|
+
y: finalY,
|
|
43
|
+
w: actualW,
|
|
44
|
+
h: actualH,
|
|
45
|
+
} = clip
|
|
46
|
+
|
|
47
|
+
const maskData = mask.data
|
|
48
|
+
const dst32 = dst.data32
|
|
49
|
+
const dw = dst.width
|
|
50
|
+
|
|
51
|
+
// Pre-calculate the alpha-adjusted color once outside the loop
|
|
52
|
+
let finalCol = color
|
|
53
|
+
|
|
54
|
+
if (alpha < 255) {
|
|
55
|
+
const baseSrcAlpha = color >>> 24
|
|
56
|
+
const colorRGB = color & 0x00ffffff
|
|
57
|
+
const a = (baseSrcAlpha * alpha + 128) >> 8
|
|
58
|
+
|
|
59
|
+
finalCol = ((colorRGB | (a << 24)) >>> 0) as Color32
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (let iy = 0; iy < actualH; iy++) {
|
|
63
|
+
const currentY = finalY + iy
|
|
64
|
+
const maskY = currentY - y
|
|
65
|
+
const maskOffset = maskY * maskW
|
|
66
|
+
|
|
67
|
+
const dstRowOffset = currentY * dw
|
|
68
|
+
|
|
69
|
+
for (let ix = 0; ix < actualW; ix++) {
|
|
70
|
+
const currentX = finalX + ix
|
|
71
|
+
const maskX = currentX - x
|
|
72
|
+
const maskIndex = maskOffset + maskX
|
|
73
|
+
|
|
74
|
+
if (maskData[maskIndex]) {
|
|
75
|
+
dst32[dstRowOffset + currentX] = finalCol
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -14,7 +14,6 @@ export function invertPixelData(
|
|
|
14
14
|
w: width = pixelData.width,
|
|
15
15
|
h: height = pixelData.height,
|
|
16
16
|
mask,
|
|
17
|
-
mw,
|
|
18
17
|
mx = 0,
|
|
19
18
|
my = 0,
|
|
20
19
|
invertMask = false,
|
|
@@ -33,7 +32,7 @@ export function invertPixelData(
|
|
|
33
32
|
|
|
34
33
|
const dst32 = dst.data32
|
|
35
34
|
const dw = dst.width
|
|
36
|
-
const mPitch =
|
|
35
|
+
const mPitch = mask?.w ?? width
|
|
37
36
|
|
|
38
37
|
const dx = x - targetX
|
|
39
38
|
const dy = y - targetY
|
|
@@ -46,9 +45,10 @@ export function invertPixelData(
|
|
|
46
45
|
|
|
47
46
|
// Optimization: Split loops to avoid checking `if (mask)` for every pixel.
|
|
48
47
|
if (mask) {
|
|
48
|
+
const maskData = mask.data
|
|
49
49
|
for (let iy = 0; iy < actualH; iy++) {
|
|
50
50
|
for (let ix = 0; ix < actualW; ix++) {
|
|
51
|
-
const mVal =
|
|
51
|
+
const mVal = maskData[mIdx]
|
|
52
52
|
const isHit = invertMask
|
|
53
53
|
? mVal === 0
|
|
54
54
|
: mVal === 1
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AlphaMask, IPixelData } from '../_types'
|
|
2
|
+
import { makeAlphaMask } from '../Mask/AlphaMask'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Extracts the alpha channel from PixelData into a single-channel mask.
|
|
@@ -13,14 +14,15 @@ export function pixelDataToAlphaMask(
|
|
|
13
14
|
height,
|
|
14
15
|
} = pixelData
|
|
15
16
|
const len = data32.length
|
|
16
|
-
const mask =
|
|
17
|
+
const mask = makeAlphaMask(width, height)
|
|
18
|
+
const maskData = mask.data
|
|
17
19
|
|
|
18
20
|
for (let i = 0; i < len; i++) {
|
|
19
21
|
const val = data32[i]
|
|
20
22
|
|
|
21
23
|
// Extract the Alpha byte (top 8 bits in ABGR / Little-Endian)
|
|
22
24
|
// Shift right by 24 moves the 4th byte to the 1st position
|
|
23
|
-
|
|
25
|
+
maskData[i] = (val >>> 24) & 0xff
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
return mask
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { type IPixelData, type Rect } from '../_types'
|
|
2
2
|
import { makeClippedBlit, resolveBlitClipping } from '../Internal/resolveClipping'
|
|
3
|
-
import type { PixelData } from './PixelData'
|
|
4
3
|
|
|
5
4
|
const SCRATCH_BLIT = makeClippedBlit()
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
* Copies a pixel buffer into a specific region of a {@link
|
|
7
|
+
* Copies a pixel buffer into a specific region of a {@link IPixelData} object.
|
|
9
8
|
*
|
|
10
9
|
* This function performs a direct memory copy from a {@link Uint32Array}
|
|
11
|
-
* into the target
|
|
10
|
+
* into the target buffer.
|
|
12
11
|
*/
|
|
13
12
|
export function writePixelDataBuffer(
|
|
14
13
|
target: IPixelData,
|