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
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Color32, type ImageDataLike, MaskType, type Rect
|
|
1
|
+
import { type BinaryMaskRect, type Color32, type ImageDataLike, 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'
|
|
@@ -13,7 +13,7 @@ export type FloodFillImageDataOptions = {
|
|
|
13
13
|
export type FloodFillResult = {
|
|
14
14
|
startX: number
|
|
15
15
|
startY: number
|
|
16
|
-
selectionRect:
|
|
16
|
+
selectionRect: BinaryMaskRect
|
|
17
17
|
pixels: Uint8ClampedArray
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -27,9 +27,9 @@ export type FloodFillResult = {
|
|
|
27
27
|
* @param startX - The starting horizontal coordinate.
|
|
28
28
|
* @param startY - The starting vertical coordinate.
|
|
29
29
|
* @param options - Configuration for the fill operation.
|
|
30
|
-
* @param options.contiguous -
|
|
30
|
+
* @param options.contiguous - If true, only connected pixels are
|
|
31
31
|
* selected. If false, all pixels within tolerance are selected regardless of position.
|
|
32
|
-
* @param options.tolerance -
|
|
32
|
+
* @param options.tolerance - The maximum allowed difference in color
|
|
33
33
|
* distance (0-255) for a pixel to be included.
|
|
34
34
|
* @param options.bounds - Optional bounding box to restrict the search area.
|
|
35
35
|
*
|
|
@@ -181,20 +181,20 @@ export function floodFillSelection(
|
|
|
181
181
|
if (matchCount === 0) {
|
|
182
182
|
return null
|
|
183
183
|
}
|
|
184
|
-
const
|
|
184
|
+
const w = maxX - minX + 1
|
|
185
|
+
const h = maxY - minY + 1
|
|
186
|
+
const selectionRect: BinaryMaskRect = {
|
|
185
187
|
x: minX,
|
|
186
188
|
y: minY,
|
|
187
|
-
w
|
|
188
|
-
h
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
w,
|
|
190
|
+
h,
|
|
191
|
+
data: new Uint8Array(w * h),
|
|
192
|
+
type: MaskType.BINARY,
|
|
191
193
|
}
|
|
192
194
|
|
|
193
|
-
// REMOVED trimRectBounds from here
|
|
194
|
-
|
|
195
195
|
const sw = selectionRect.w
|
|
196
196
|
const sh = selectionRect.h
|
|
197
|
-
const finalMask = selectionRect.
|
|
197
|
+
const finalMask = selectionRect.data
|
|
198
198
|
|
|
199
199
|
for (let i = 0; i < matchCount; i++) {
|
|
200
200
|
const mx = matchX[i] - selectionRect.x
|
|
@@ -205,13 +205,11 @@ export function floodFillSelection(
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
// trimRectBounds can see them and work correctly.
|
|
209
208
|
trimRectBounds(
|
|
210
209
|
selectionRect,
|
|
211
210
|
{ x: 0, y: 0, w: width, h: height },
|
|
212
211
|
)
|
|
213
212
|
|
|
214
|
-
// Use the UPDATED values from the selectionRect after trimming
|
|
215
213
|
const extracted = extractImageDataBuffer(
|
|
216
214
|
imageData,
|
|
217
215
|
selectionRect.x,
|
|
@@ -12,14 +12,7 @@ export function toBlendModeIndexAndName(input: string | number) {
|
|
|
12
12
|
const isNumeric = trimmed !== '' && !Number.isNaN(num)
|
|
13
13
|
|
|
14
14
|
if (isNumeric && Number.isInteger(num)) {
|
|
15
|
-
console.log({
|
|
16
|
-
trimmed,
|
|
17
|
-
num,
|
|
18
|
-
isNumeric,
|
|
19
|
-
isInt: Number.isInteger(num),
|
|
20
|
-
})
|
|
21
15
|
const name = getKeyByValue(BaseBlendMode, num)
|
|
22
|
-
console.log({name})
|
|
23
16
|
if (name === undefined) throw new Error(`Invalid index: ${num}`)
|
|
24
17
|
return { blendIndex: num, blendName: name }
|
|
25
18
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Writes a {@link Blob} image to the system clipboard.
|
|
3
3
|
*
|
|
4
|
-
* @param blob - The image
|
|
4
|
+
* @param blob - The image blob (typically `image/png`) to copy.
|
|
5
5
|
* @returns A promise that resolves when the clipboard has been updated.
|
|
6
6
|
*/
|
|
7
7
|
export async function writeImgBlobToClipboard(blob: Blob): Promise<void> {
|
|
@@ -8,6 +8,9 @@ const defaults = {
|
|
|
8
8
|
|
|
9
9
|
type Deps = Partial<typeof defaults>
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @param deps - @hidden
|
|
13
|
+
*/
|
|
11
14
|
export const mutatorApplyAlphaMask = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
|
|
12
15
|
const {
|
|
13
16
|
applyAlphaMaskToPixelData = defaults.applyAlphaMaskToPixelData,
|
|
@@ -8,6 +8,9 @@ const defaults = {
|
|
|
8
8
|
|
|
9
9
|
type Deps = Partial<typeof defaults>
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @param deps - @hidden
|
|
13
|
+
*/
|
|
11
14
|
export const mutatorApplyBinaryMask = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
|
|
12
15
|
const {
|
|
13
16
|
applyBinaryMaskToPixelData = defaults.applyBinaryMaskToPixelData,
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BlendColor32,
|
|
3
|
+
CircleBrushAlphaMask,
|
|
4
|
+
Color32,
|
|
5
|
+
ColorBlendMaskOptions,
|
|
6
|
+
HistoryMutator,
|
|
7
|
+
Rect,
|
|
8
|
+
} from '../../_types'
|
|
9
|
+
import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
|
|
2
10
|
import { applyCircleBrushToPixelData } from '../../PixelData/applyCircleBrushToPixelData'
|
|
3
11
|
import { getCircleBrushOrPencilBounds } from '../../Rect/getCircleBrushOrPencilBounds'
|
|
4
12
|
import { PixelWriter } from '../PixelWriter'
|
|
@@ -10,6 +18,9 @@ const defaults = {
|
|
|
10
18
|
|
|
11
19
|
type Deps = Partial<typeof defaults>
|
|
12
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @param deps - @hidden
|
|
23
|
+
*/
|
|
13
24
|
export const mutatorApplyCircleBrush = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
|
|
14
25
|
const {
|
|
15
26
|
applyCircleBrushToPixelData = defaults.applyCircleBrushToPixelData,
|
|
@@ -19,21 +30,29 @@ export const mutatorApplyCircleBrush = ((writer: PixelWriter<any>, deps: Deps =
|
|
|
19
30
|
|
|
20
31
|
const boundsOut: Rect = { x: 0, y: 0, w: 0, h: 0 }
|
|
21
32
|
|
|
33
|
+
const blendColorPixelOptions: ColorBlendMaskOptions = {
|
|
34
|
+
alpha: 255,
|
|
35
|
+
blendFn: sourceOverPerfect,
|
|
36
|
+
x: 0,
|
|
37
|
+
y: 0,
|
|
38
|
+
w: 0,
|
|
39
|
+
h: 0,
|
|
40
|
+
}
|
|
41
|
+
|
|
22
42
|
return {
|
|
23
43
|
applyCircleBrush(
|
|
24
44
|
color: Color32,
|
|
25
45
|
centerX: number,
|
|
26
46
|
centerY: number,
|
|
27
|
-
|
|
47
|
+
brush: CircleBrushAlphaMask,
|
|
28
48
|
alpha = 255,
|
|
29
|
-
fallOff: (dist: number) => number,
|
|
30
49
|
blendFn?: BlendColor32,
|
|
31
50
|
) {
|
|
32
51
|
|
|
33
52
|
const bounds = getCircleBrushOrPencilBounds(
|
|
34
53
|
centerX,
|
|
35
54
|
centerY,
|
|
36
|
-
|
|
55
|
+
brush.size,
|
|
37
56
|
writer.target.width,
|
|
38
57
|
writer.target.height,
|
|
39
58
|
boundsOut,
|
|
@@ -48,10 +67,10 @@ export const mutatorApplyCircleBrush = ((writer: PixelWriter<any>, deps: Deps =
|
|
|
48
67
|
color,
|
|
49
68
|
centerX,
|
|
50
69
|
centerY,
|
|
51
|
-
|
|
70
|
+
brush,
|
|
52
71
|
alpha,
|
|
53
|
-
fallOff,
|
|
54
72
|
blendFn,
|
|
73
|
+
blendColorPixelOptions,
|
|
55
74
|
bounds,
|
|
56
75
|
)
|
|
57
76
|
},
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AlphaMask,
|
|
3
3
|
type BlendColor32,
|
|
4
|
+
type CircleBrushAlphaMask,
|
|
4
5
|
type Color32,
|
|
5
6
|
type ColorBlendMaskOptions,
|
|
6
7
|
type HistoryMutator,
|
|
8
|
+
MaskType,
|
|
7
9
|
type Rect,
|
|
8
10
|
} from '../../_types'
|
|
9
11
|
import { forEachLinePoint } from '../../Algorithm/forEachLinePoint'
|
|
@@ -22,6 +24,9 @@ const defaults = {
|
|
|
22
24
|
|
|
23
25
|
type Deps = Partial<typeof defaults>
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* @param deps - @hidden
|
|
29
|
+
*/
|
|
25
30
|
export const mutatorApplyCircleBrushStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
|
|
26
31
|
const {
|
|
27
32
|
forEachLinePoint = defaults.forEachLinePoint,
|
|
@@ -53,6 +58,13 @@ export const mutatorApplyCircleBrushStroke = ((writer: PixelWriter<any>, deps: D
|
|
|
53
58
|
h: 0,
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
const mask = {
|
|
62
|
+
type: MaskType.ALPHA,
|
|
63
|
+
data: null as unknown as Uint8Array,
|
|
64
|
+
w: 0,
|
|
65
|
+
h: 0,
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
return {
|
|
57
69
|
applyCircleBrushStroke(
|
|
58
70
|
color: Color32,
|
|
@@ -60,70 +72,96 @@ export const mutatorApplyCircleBrushStroke = ((writer: PixelWriter<any>, deps: D
|
|
|
60
72
|
y0: number,
|
|
61
73
|
x1: number,
|
|
62
74
|
y1: number,
|
|
63
|
-
|
|
75
|
+
brush: CircleBrushAlphaMask,
|
|
64
76
|
alpha = 255,
|
|
65
|
-
fallOff: (dist: number) => number,
|
|
66
77
|
blendFn: BlendColor32 = sourceOverPerfect,
|
|
67
78
|
) {
|
|
79
|
+
const brushSize = brush.size
|
|
80
|
+
|
|
68
81
|
const {
|
|
69
82
|
x: bx,
|
|
70
83
|
y: by,
|
|
71
84
|
w: bw,
|
|
72
85
|
h: bh,
|
|
73
|
-
} = getCircleBrushOrPencilStrokeBounds(
|
|
86
|
+
} = getCircleBrushOrPencilStrokeBounds(
|
|
87
|
+
x0,
|
|
88
|
+
y0,
|
|
89
|
+
x1,
|
|
90
|
+
y1,
|
|
91
|
+
brushSize,
|
|
92
|
+
strokeBoundsOut,
|
|
93
|
+
)
|
|
74
94
|
|
|
75
95
|
if (bw <= 0 || bh <= 0) return
|
|
76
96
|
|
|
77
|
-
|
|
97
|
+
mask.data = new Uint8Array(bw * bh)
|
|
98
|
+
mask.w = bw
|
|
99
|
+
mask.h = bh
|
|
78
100
|
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const centerOffset = (brushSize % 2 === 0) ? 0.5 : 0
|
|
101
|
+
const maskData = mask.data
|
|
102
|
+
const brushData = brush.data
|
|
103
|
+
const minOffset = brush.minOffset
|
|
83
104
|
|
|
84
105
|
const targetWidth = writer.target.width
|
|
85
106
|
const targetHeight = writer.target.height
|
|
86
107
|
|
|
87
|
-
forEachLinePoint(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
108
|
+
forEachLinePoint(
|
|
109
|
+
x0,
|
|
110
|
+
y0,
|
|
111
|
+
x1,
|
|
112
|
+
y1,
|
|
113
|
+
(px, py) => {
|
|
114
|
+
const {
|
|
115
|
+
x: cbx,
|
|
116
|
+
y: cby,
|
|
117
|
+
w: cbw,
|
|
118
|
+
h: cbh,
|
|
119
|
+
} = getCircleBrushOrPencilBounds(
|
|
120
|
+
px,
|
|
121
|
+
py,
|
|
122
|
+
brushSize,
|
|
123
|
+
targetWidth,
|
|
124
|
+
targetHeight,
|
|
125
|
+
circleBrushBounds,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
writer.accumulator.storeRegionBeforeState(
|
|
129
|
+
cbx,
|
|
130
|
+
cby,
|
|
131
|
+
cbw,
|
|
132
|
+
cbh,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const startX = Math.max(bx, cbx)
|
|
136
|
+
const startY = Math.max(by, cby)
|
|
137
|
+
const endX = Math.min(bx + bw, cbx + cbw)
|
|
138
|
+
const endY = Math.min(by + bh, cby + cbh)
|
|
139
|
+
|
|
140
|
+
const unclippedStartX = Math.floor(px + minOffset)
|
|
141
|
+
const unclippedStartY = Math.floor(py + minOffset)
|
|
142
|
+
|
|
143
|
+
for (let my = startY; my < endY; my++) {
|
|
144
|
+
const strokeMaskY = my - by
|
|
145
|
+
const strokeMaskRowOffset = strokeMaskY * bw
|
|
146
|
+
|
|
147
|
+
const brushY = my - unclippedStartY
|
|
148
|
+
const brushRowOffset = brushY * brushSize
|
|
149
|
+
|
|
150
|
+
for (let mx = startX; mx < endX; mx++) {
|
|
151
|
+
const brushX = mx - unclippedStartX
|
|
152
|
+
const brushVal = brushData[brushRowOffset + brushX]
|
|
153
|
+
|
|
154
|
+
if (brushVal > 0) {
|
|
155
|
+
const strokeMaskIdx = strokeMaskRowOffset + (mx - bx)
|
|
156
|
+
|
|
157
|
+
if (brushVal > maskData[strokeMaskIdx]) {
|
|
158
|
+
maskData[strokeMaskIdx] = brushVal
|
|
159
|
+
}
|
|
122
160
|
}
|
|
123
161
|
}
|
|
124
162
|
}
|
|
125
|
-
}
|
|
126
|
-
|
|
163
|
+
},
|
|
164
|
+
)
|
|
127
165
|
|
|
128
166
|
blendColorPixelOptions.blendFn = blendFn
|
|
129
167
|
blendColorPixelOptions.alpha = alpha
|
|
@@ -132,7 +170,12 @@ export const mutatorApplyCircleBrushStroke = ((writer: PixelWriter<any>, deps: D
|
|
|
132
170
|
blendColorPixelOptions.w = bw
|
|
133
171
|
blendColorPixelOptions.h = bh
|
|
134
172
|
|
|
135
|
-
blendColorPixelDataAlphaMask(
|
|
173
|
+
blendColorPixelDataAlphaMask(
|
|
174
|
+
writer.target,
|
|
175
|
+
color,
|
|
176
|
+
mask as AlphaMask,
|
|
177
|
+
blendColorPixelOptions,
|
|
178
|
+
)
|
|
136
179
|
},
|
|
137
180
|
}
|
|
138
181
|
}) satisfies HistoryMutator<any, Deps>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BlendColor32, Color32, HistoryMutator, Rect } from '../../_types'
|
|
1
|
+
import type { BlendColor32, CircleBrushMask, Color32, HistoryMutator, Rect } from '../../_types'
|
|
2
2
|
import { applyCircleBrushToPixelData } from '../../PixelData/applyCircleBrushToPixelData'
|
|
3
3
|
import { getCircleBrushOrPencilBounds } from '../../Rect/getCircleBrushOrPencilBounds'
|
|
4
4
|
import { PixelWriter } from '../PixelWriter'
|
|
@@ -6,16 +6,17 @@ import { PixelWriter } from '../PixelWriter'
|
|
|
6
6
|
const defaults = {
|
|
7
7
|
applyCircleBrushToPixelData,
|
|
8
8
|
getCircleBrushOrPencilBounds,
|
|
9
|
-
fallOff: () => 1,
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
type Deps = Partial<typeof defaults>
|
|
13
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @param deps - @hidden
|
|
15
|
+
**/
|
|
14
16
|
export const mutatorApplyCirclePencil = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
|
|
15
17
|
const {
|
|
16
18
|
applyCircleBrushToPixelData = defaults.applyCircleBrushToPixelData,
|
|
17
19
|
getCircleBrushOrPencilBounds = defaults.getCircleBrushOrPencilBounds,
|
|
18
|
-
fallOff = defaults.fallOff,
|
|
19
20
|
} = deps
|
|
20
21
|
|
|
21
22
|
const boundsOut: Rect = { x: 0, y: 0, w: 0, h: 0 }
|
|
@@ -25,7 +26,7 @@ export const mutatorApplyCirclePencil = ((writer: PixelWriter<any>, deps: Deps =
|
|
|
25
26
|
color: Color32,
|
|
26
27
|
centerX: number,
|
|
27
28
|
centerY: number,
|
|
28
|
-
|
|
29
|
+
brush: CircleBrushMask,
|
|
29
30
|
alpha = 255,
|
|
30
31
|
blendFn?: BlendColor32,
|
|
31
32
|
) {
|
|
@@ -33,7 +34,7 @@ export const mutatorApplyCirclePencil = ((writer: PixelWriter<any>, deps: Deps =
|
|
|
33
34
|
const bounds = getCircleBrushOrPencilBounds(
|
|
34
35
|
centerX,
|
|
35
36
|
centerY,
|
|
36
|
-
|
|
37
|
+
brush.size,
|
|
37
38
|
writer.target.width,
|
|
38
39
|
writer.target.height,
|
|
39
40
|
boundsOut,
|
|
@@ -48,9 +49,8 @@ export const mutatorApplyCirclePencil = ((writer: PixelWriter<any>, deps: Deps =
|
|
|
48
49
|
color,
|
|
49
50
|
centerX,
|
|
50
51
|
centerY,
|
|
51
|
-
|
|
52
|
+
brush,
|
|
52
53
|
alpha,
|
|
53
|
-
fallOff,
|
|
54
54
|
blendFn,
|
|
55
55
|
bounds,
|
|
56
56
|
)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type BinaryMask,
|
|
3
3
|
type BlendColor32,
|
|
4
|
+
type CircleBrushBinaryMask,
|
|
4
5
|
type Color32,
|
|
5
6
|
type ColorBlendMaskOptions,
|
|
6
7
|
type HistoryMutator,
|
|
8
|
+
MaskType,
|
|
7
9
|
type Rect,
|
|
8
10
|
} from '../../_types'
|
|
9
11
|
import { forEachLinePoint } from '../../Algorithm/forEachLinePoint'
|
|
@@ -22,6 +24,9 @@ const defaults = {
|
|
|
22
24
|
|
|
23
25
|
type Deps = Partial<typeof defaults>
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* @param deps - @hidden
|
|
29
|
+
*/
|
|
25
30
|
export const mutatorApplyCirclePencilStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
|
|
26
31
|
const {
|
|
27
32
|
forEachLinePoint = defaults.forEachLinePoint,
|
|
@@ -53,6 +58,13 @@ export const mutatorApplyCirclePencilStroke = ((writer: PixelWriter<any>, deps:
|
|
|
53
58
|
h: 0,
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
const mask = {
|
|
62
|
+
type: MaskType.BINARY,
|
|
63
|
+
data: null as unknown as Uint8Array,
|
|
64
|
+
w: 0,
|
|
65
|
+
h: 0,
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
return {
|
|
57
69
|
applyCirclePencilStroke(
|
|
58
70
|
color: Color32,
|
|
@@ -60,7 +72,7 @@ export const mutatorApplyCirclePencilStroke = ((writer: PixelWriter<any>, deps:
|
|
|
60
72
|
y0: number,
|
|
61
73
|
x1: number,
|
|
62
74
|
y1: number,
|
|
63
|
-
|
|
75
|
+
brush: CircleBrushBinaryMask,
|
|
64
76
|
alpha = 255,
|
|
65
77
|
blendFn: BlendColor32 = sourceOverPerfect,
|
|
66
78
|
) {
|
|
@@ -69,54 +81,77 @@ export const mutatorApplyCirclePencilStroke = ((writer: PixelWriter<any>, deps:
|
|
|
69
81
|
y: by,
|
|
70
82
|
w: bw,
|
|
71
83
|
h: bh,
|
|
72
|
-
} = getCircleBrushOrPencilStrokeBounds(
|
|
84
|
+
} = getCircleBrushOrPencilStrokeBounds(
|
|
85
|
+
x0,
|
|
86
|
+
y0,
|
|
87
|
+
x1,
|
|
88
|
+
y1,
|
|
89
|
+
brush.size,
|
|
90
|
+
strokeBoundsOut,
|
|
91
|
+
)
|
|
73
92
|
|
|
74
93
|
if (bw <= 0 || bh <= 0) return
|
|
75
94
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const rSqr = r * r
|
|
80
|
-
const centerOffset = (brushSize % 2 === 0) ? 0.5 : 0
|
|
95
|
+
mask.data = new Uint8Array(bw * bh)
|
|
96
|
+
mask.w = bw
|
|
97
|
+
mask.h = bh
|
|
81
98
|
|
|
99
|
+
const maskData = mask.data
|
|
82
100
|
const targetWidth = writer.target.width
|
|
83
101
|
const targetHeight = writer.target.height
|
|
84
102
|
|
|
85
|
-
forEachLinePoint(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
forEachLinePoint(
|
|
104
|
+
x0,
|
|
105
|
+
y0,
|
|
106
|
+
x1,
|
|
107
|
+
y1,
|
|
108
|
+
(px, py) => {
|
|
109
|
+
const {
|
|
110
|
+
x: cbx,
|
|
111
|
+
y: cby,
|
|
112
|
+
w: cbw,
|
|
113
|
+
h: cbh,
|
|
114
|
+
} = getCircleBrushOrPencilBounds(
|
|
115
|
+
px,
|
|
116
|
+
py,
|
|
117
|
+
brush.size,
|
|
118
|
+
targetWidth,
|
|
119
|
+
targetHeight,
|
|
120
|
+
circlePencilBounds,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
writer.accumulator.storeRegionBeforeState(
|
|
124
|
+
cbx,
|
|
125
|
+
cby,
|
|
126
|
+
cbw,
|
|
127
|
+
cbh,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
const unclippedStartX = Math.floor(px + brush.minOffset)
|
|
131
|
+
const unclippedStartY = Math.floor(py + brush.minOffset)
|
|
132
|
+
|
|
133
|
+
const startX = Math.max(bx, unclippedStartX)
|
|
134
|
+
const startY = Math.max(by, unclippedStartY)
|
|
135
|
+
const endX = Math.min(bx + bw, unclippedStartX + brush.w)
|
|
136
|
+
const endY = Math.min(by + bh, unclippedStartY + brush.h)
|
|
137
|
+
|
|
138
|
+
for (let my = startY; my < endY; my++) {
|
|
139
|
+
const brushY = my - unclippedStartY
|
|
140
|
+
const maskRowOffset = (my - by) * bw
|
|
141
|
+
const brushRowOffset = brushY * brush.w
|
|
142
|
+
|
|
143
|
+
for (let mx = startX; mx < endX; mx++) {
|
|
144
|
+
const brushX = mx - unclippedStartX
|
|
145
|
+
const brushAlpha = brush.data[brushRowOffset + brushX]
|
|
146
|
+
|
|
147
|
+
if (brushAlpha > 0) {
|
|
148
|
+
const maskIdx = maskRowOffset + (mx - bx)
|
|
149
|
+
maskData[maskIdx] = brushAlpha
|
|
150
|
+
}
|
|
116
151
|
}
|
|
117
152
|
}
|
|
118
|
-
}
|
|
119
|
-
|
|
153
|
+
},
|
|
154
|
+
)
|
|
120
155
|
|
|
121
156
|
blendColorPixelOptions.blendFn = blendFn
|
|
122
157
|
blendColorPixelOptions.alpha = alpha
|
|
@@ -125,7 +160,12 @@ export const mutatorApplyCirclePencilStroke = ((writer: PixelWriter<any>, deps:
|
|
|
125
160
|
blendColorPixelOptions.w = bw
|
|
126
161
|
blendColorPixelOptions.h = bh
|
|
127
162
|
|
|
128
|
-
blendColorPixelDataBinaryMask(
|
|
163
|
+
blendColorPixelDataBinaryMask(
|
|
164
|
+
writer.target,
|
|
165
|
+
color,
|
|
166
|
+
mask as BinaryMask,
|
|
167
|
+
blendColorPixelOptions,
|
|
168
|
+
)
|
|
129
169
|
},
|
|
130
170
|
}
|
|
131
171
|
}) satisfies HistoryMutator<any, Deps>
|
|
@@ -10,6 +10,9 @@ const defaults = {
|
|
|
10
10
|
|
|
11
11
|
type Deps = Partial<typeof defaults>
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @param deps - @hidden
|
|
15
|
+
*/
|
|
13
16
|
export const mutatorApplyRectBrush = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
|
|
14
17
|
const {
|
|
15
18
|
applyRectBrushToPixelData = defaults.applyRectBrushToPixelData,
|