pixel-data-js 0.24.0 → 0.25.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 +1476 -1834
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +1465 -1816
- package/dist/index.dev.js.map +1 -1
- package/dist/index.prod.cjs +1475 -1833
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +233 -310
- package/dist/index.prod.js +1465 -1816
- package/dist/index.prod.js.map +1 -1
- package/package.json +1 -1
- package/src/Algorithm/floodFillSelection.ts +2 -2
- package/src/Canvas/canvas-blend-modes.ts +28 -0
- package/src/History/PixelAccumulator.ts +52 -29
- package/src/History/PixelEngineConfig.ts +7 -9
- package/src/History/PixelMutator/mutatorBlendPaintMask.ts +60 -0
- package/src/History/PixelMutator/mutatorBlendPixelData.ts +2 -2
- package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +2 -2
- package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +2 -2
- package/src/History/PixelMutator.ts +0 -20
- package/src/History/PixelPatchTiles.ts +2 -2
- package/src/History/PixelWriter.ts +132 -9
- package/src/Internal/helpers.ts +2 -0
- package/src/Paint/PaintBuffer.ts +269 -0
- package/src/{PixelTile/PaintBufferRenderer.ts → Paint/PaintBufferCanvasRenderer.ts} +13 -5
- package/src/Paint/makeCirclePaintAlphaMask.ts +41 -0
- package/src/{Mask/CircleBinaryMask.ts → Paint/makeCirclePaintBinaryMask.ts} +5 -6
- package/src/Paint/makePaintMask.ts +28 -0
- package/src/Paint/makeRectFalloffPaintAlphaMask.ts +47 -0
- package/src/PixelData/PixelBuffer32.ts +2 -2
- package/src/PixelData/PixelData.ts +1 -1
- package/src/PixelData/applyAlphaMaskToPixelData.ts +2 -2
- package/src/PixelData/applyBinaryMaskToPixelData.ts +2 -2
- package/src/PixelData/blendColorPixelData.ts +2 -2
- package/src/PixelData/blendColorPixelDataAlphaMask.ts +3 -3
- package/src/PixelData/blendColorPixelDataBinaryMask.ts +3 -3
- package/src/PixelData/blendPixel.ts +2 -2
- package/src/PixelData/blendPixelData.ts +3 -3
- package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
- package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
- package/src/PixelData/blendPixelDataPaintBuffer.ts +3 -3
- package/src/PixelData/clearPixelData.ts +2 -2
- package/src/PixelData/extractPixelData.ts +4 -4
- package/src/PixelData/extractPixelDataBuffer.ts +4 -4
- package/src/PixelData/fillPixelData.ts +5 -5
- package/src/PixelData/fillPixelDataBinaryMask.ts +3 -3
- package/src/PixelData/fillPixelDataFast.ts +5 -5
- package/src/PixelData/invertPixelData.ts +2 -2
- package/src/PixelData/pixelDataToAlphaMask.ts +2 -2
- package/src/PixelData/reflectPixelData.ts +3 -3
- package/src/PixelData/resamplePixelData.ts +2 -2
- package/src/PixelData/writePaintBufferToPixelData.ts +26 -0
- package/src/PixelData/writePixelDataBuffer.ts +5 -5
- package/src/Rect/trimMaskRectBounds.ts +121 -0
- package/src/Rect/trimRectBounds.ts +25 -116
- package/src/_types.ts +16 -15
- package/src/index.ts +11 -24
- package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +0 -182
- package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +0 -59
- package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +0 -172
- package/src/History/PixelMutator/mutatorApplyRectBrush.ts +0 -64
- package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +0 -184
- package/src/History/PixelMutator/mutatorApplyRectPencil.ts +0 -65
- package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +0 -166
- package/src/History/PixelMutator/mutatorBlendColorCircleMask.ts +0 -71
- package/src/Mask/CircleAlphaMask.ts +0 -32
- package/src/PixelData/applyRectBrushToPixelData.ts +0 -98
- package/src/PixelData/blendColorPixelDataCircleMask.ts +0 -92
- package/src/PixelTile/PaintBuffer.ts +0 -122
- package/src/Rect/getCircleBrushOrPencilBounds.ts +0 -43
- package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +0 -24
- package/src/Rect/getRectBrushOrPencilBounds.ts +0 -38
- package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +0 -26
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import type { Color32, PaintAlphaMask, PaintBinaryMask, Rect } from '../_types'
|
|
2
|
+
import { forEachLinePoint } from '../Algorithm/forEachLinePoint'
|
|
3
|
+
import type { PixelEngineConfig } from '../History/PixelEngineConfig'
|
|
4
|
+
import { macro_halfAndFloor } from '../Internal/helpers'
|
|
5
|
+
import type { PixelTile } from '../PixelTile/PixelTile'
|
|
6
|
+
import type { PixelTilePool } from '../PixelTile/PixelTilePool'
|
|
7
|
+
import { trimRectBounds } from '../Rect/trimRectBounds'
|
|
8
|
+
|
|
9
|
+
export class PaintBuffer {
|
|
10
|
+
readonly lookup: (PixelTile | undefined)[]
|
|
11
|
+
private readonly scratchBounds: Rect = { x: 0, y: 0, w: 0, h: 0 }
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
readonly config: PixelEngineConfig,
|
|
15
|
+
readonly tilePool: PixelTilePool,
|
|
16
|
+
) {
|
|
17
|
+
this.lookup = []
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private eachTileInBounds(
|
|
21
|
+
bounds: Rect,
|
|
22
|
+
callback: (tile: PixelTile, bX: number, bY: number, bW: number, bH: number) => void,
|
|
23
|
+
): void {
|
|
24
|
+
const { tileShift, targetColumns, targetRows, tileSize } = this.config
|
|
25
|
+
|
|
26
|
+
const x1 = Math.max(0, bounds.x >> tileShift)
|
|
27
|
+
const y1 = Math.max(0, bounds.y >> tileShift)
|
|
28
|
+
const x2 = Math.min(targetColumns - 1, (bounds.x + bounds.w - 1) >> tileShift)
|
|
29
|
+
const y2 = Math.min(targetRows - 1, (bounds.y + bounds.h - 1) >> tileShift)
|
|
30
|
+
|
|
31
|
+
if (x1 > x2 || y1 > y2) return
|
|
32
|
+
|
|
33
|
+
const lookup = this.lookup
|
|
34
|
+
const tilePool = this.tilePool
|
|
35
|
+
|
|
36
|
+
for (let ty = y1; ty <= y2; ty++) {
|
|
37
|
+
const rowOffset = ty * targetColumns
|
|
38
|
+
const tileTop = ty << tileShift
|
|
39
|
+
|
|
40
|
+
for (let tx = x1; tx <= x2; tx++) {
|
|
41
|
+
const id = rowOffset + tx
|
|
42
|
+
const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty))
|
|
43
|
+
const tileLeft = tx << tileShift
|
|
44
|
+
|
|
45
|
+
const startX = bounds.x > tileLeft ? bounds.x : tileLeft
|
|
46
|
+
const startY = bounds.y > tileTop ? bounds.y : tileTop
|
|
47
|
+
|
|
48
|
+
const maskEndX = bounds.x + bounds.w
|
|
49
|
+
const tileEndX = tileLeft + tileSize
|
|
50
|
+
const endX = maskEndX < tileEndX ? maskEndX : tileEndX
|
|
51
|
+
|
|
52
|
+
const maskEndY = bounds.y + bounds.h
|
|
53
|
+
const tileEndY = tileTop + tileSize
|
|
54
|
+
const endY = maskEndY < tileEndY ? maskEndY : tileEndY
|
|
55
|
+
|
|
56
|
+
callback(tile, startX, startY, endX - startX, endY - startY)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
writePaintAlphaMaskStroke(
|
|
62
|
+
color: Color32,
|
|
63
|
+
brush: PaintAlphaMask,
|
|
64
|
+
x0: number,
|
|
65
|
+
y0: number,
|
|
66
|
+
x1: number,
|
|
67
|
+
y1: number,
|
|
68
|
+
): boolean {
|
|
69
|
+
const cA = color >>> 24
|
|
70
|
+
if (cA === 0) return false
|
|
71
|
+
|
|
72
|
+
const { tileShift, tileMask, target } = this.config
|
|
73
|
+
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
74
|
+
const cRGB = color & 0x00ffffff
|
|
75
|
+
const scratch = this.scratchBounds
|
|
76
|
+
|
|
77
|
+
let changed = false
|
|
78
|
+
|
|
79
|
+
forEachLinePoint(x0, y0, x1, y1, (px, py) => {
|
|
80
|
+
|
|
81
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
82
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
83
|
+
trimRectBounds(
|
|
84
|
+
topLeftX,
|
|
85
|
+
topLeftY,
|
|
86
|
+
bW,
|
|
87
|
+
bH,
|
|
88
|
+
target.width,
|
|
89
|
+
target.height,
|
|
90
|
+
scratch,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
94
|
+
|
|
95
|
+
this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
96
|
+
const d32 = tile.data32
|
|
97
|
+
let tileChanged = false
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < bH_t; i++) {
|
|
100
|
+
const canvasY = bY + i
|
|
101
|
+
const bOff = (canvasY - topLeftY) * bW
|
|
102
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
103
|
+
const dS = tOff + (bX & tileMask)
|
|
104
|
+
|
|
105
|
+
for (let j = 0; j < bW_t; j++) {
|
|
106
|
+
const canvasX = bX + j
|
|
107
|
+
const brushA = bD[bOff + (canvasX - topLeftX)]
|
|
108
|
+
if (brushA === 0) continue
|
|
109
|
+
|
|
110
|
+
const t = cA * brushA + 128
|
|
111
|
+
const blendedA = (t + (t >> 8)) >> 8
|
|
112
|
+
|
|
113
|
+
const idx = dS + j
|
|
114
|
+
const cur = d32[idx]
|
|
115
|
+
if (brushA > (cur >>> 24)) {
|
|
116
|
+
const next = (cRGB | (blendedA << 24)) >>> 0
|
|
117
|
+
if (cur !== next) {
|
|
118
|
+
d32[idx] = next as Color32
|
|
119
|
+
tileChanged = true
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (tileChanged) changed = true
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
return changed
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
writePaintBinaryMaskStroke(
|
|
131
|
+
color: Color32,
|
|
132
|
+
brush: PaintBinaryMask,
|
|
133
|
+
x0: number,
|
|
134
|
+
y0: number,
|
|
135
|
+
x1: number,
|
|
136
|
+
y1: number,
|
|
137
|
+
): boolean {
|
|
138
|
+
const alphaIsZero = (color >>> 24) === 0
|
|
139
|
+
if (alphaIsZero) return false
|
|
140
|
+
|
|
141
|
+
const { tileShift, tileMask, target } = this.config
|
|
142
|
+
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
143
|
+
const scratch = this.scratchBounds
|
|
144
|
+
let changed = false
|
|
145
|
+
|
|
146
|
+
forEachLinePoint(x0, y0, x1, y1, (px, py) => {
|
|
147
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
148
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
149
|
+
|
|
150
|
+
trimRectBounds(
|
|
151
|
+
topLeftX,
|
|
152
|
+
topLeftY,
|
|
153
|
+
bW,
|
|
154
|
+
bH,
|
|
155
|
+
target.width,
|
|
156
|
+
target.height,
|
|
157
|
+
scratch,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
161
|
+
|
|
162
|
+
this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
163
|
+
const d32 = tile.data32
|
|
164
|
+
let tileChanged = false
|
|
165
|
+
|
|
166
|
+
for (let i = 0; i < bH_t; i++) {
|
|
167
|
+
const canvasY = bY + i
|
|
168
|
+
const bOff = (canvasY - topLeftY) * bW
|
|
169
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
170
|
+
const dS = tOff + (bX & tileMask)
|
|
171
|
+
|
|
172
|
+
for (let j = 0; j < bW_t; j++) {
|
|
173
|
+
const canvasX = bX + j
|
|
174
|
+
if (bD[bOff + (canvasX - topLeftX)]) {
|
|
175
|
+
const idx = dS + j
|
|
176
|
+
if (d32[idx] !== color) {
|
|
177
|
+
d32[idx] = color
|
|
178
|
+
tileChanged = true
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (tileChanged) changed = true
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
return changed
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
writeRectStroke(
|
|
191
|
+
color: Color32,
|
|
192
|
+
brushWidth: number,
|
|
193
|
+
brushHeight: number,
|
|
194
|
+
x0: number,
|
|
195
|
+
y0: number,
|
|
196
|
+
x1: number,
|
|
197
|
+
y1: number,
|
|
198
|
+
): boolean {
|
|
199
|
+
const alphaIsZero = (color >>> 24) === 0
|
|
200
|
+
if (alphaIsZero) return false
|
|
201
|
+
|
|
202
|
+
const config = this.config
|
|
203
|
+
const tileShift = config.tileShift
|
|
204
|
+
const tileMask = config.tileMask
|
|
205
|
+
const target = config.target
|
|
206
|
+
const scratch = this.scratchBounds
|
|
207
|
+
|
|
208
|
+
const centerOffsetX = macro_halfAndFloor(brushWidth - 1)
|
|
209
|
+
const centerOffsetY = macro_halfAndFloor(brushHeight - 1)
|
|
210
|
+
|
|
211
|
+
let changed = false
|
|
212
|
+
|
|
213
|
+
forEachLinePoint(
|
|
214
|
+
x0,
|
|
215
|
+
y0,
|
|
216
|
+
x1,
|
|
217
|
+
y1,
|
|
218
|
+
(px, py) => {
|
|
219
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
220
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
221
|
+
|
|
222
|
+
trimRectBounds(
|
|
223
|
+
topLeftX,
|
|
224
|
+
topLeftY,
|
|
225
|
+
brushWidth,
|
|
226
|
+
brushHeight,
|
|
227
|
+
target.width,
|
|
228
|
+
target.height,
|
|
229
|
+
scratch,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
233
|
+
|
|
234
|
+
this.eachTileInBounds(
|
|
235
|
+
scratch,
|
|
236
|
+
(tile, bX, bY, bW_t, bH_t) => {
|
|
237
|
+
const d32 = tile.data32
|
|
238
|
+
let tileChanged = false
|
|
239
|
+
|
|
240
|
+
for (let i = 0; i < bH_t; i++) {
|
|
241
|
+
const canvasY = bY + i
|
|
242
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
243
|
+
const dS = tOff + (bX & tileMask)
|
|
244
|
+
|
|
245
|
+
for (let j = 0; j < bW_t; j++) {
|
|
246
|
+
const idx = dS + j
|
|
247
|
+
|
|
248
|
+
if (d32[idx] !== color) {
|
|
249
|
+
d32[idx] = color
|
|
250
|
+
tileChanged = true
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (tileChanged) {
|
|
256
|
+
changed = true
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
)
|
|
260
|
+
},
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return changed
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
clear(): void {
|
|
267
|
+
this.tilePool.releaseTiles(this.lookup)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { CANVAS_CTX_FAILED } from '../Canvas/_constants'
|
|
2
2
|
import type { PaintBuffer } from './PaintBuffer'
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type PaintBufferCanvasRenderer = ReturnType<typeof makePaintBufferCanvasRenderer>
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
*
|
|
8
|
-
* @param paintBuffer
|
|
9
8
|
* @param offscreenCanvasClass - @internal
|
|
10
9
|
*/
|
|
11
|
-
export function
|
|
10
|
+
export function makePaintBufferCanvasRenderer(
|
|
12
11
|
paintBuffer: PaintBuffer,
|
|
13
12
|
offscreenCanvasClass = OffscreenCanvas,
|
|
14
13
|
) {
|
|
@@ -22,8 +21,14 @@ export function makePaintBufferRenderer(
|
|
|
22
21
|
ctx.imageSmoothingEnabled = false
|
|
23
22
|
|
|
24
23
|
return function drawPaintBuffer(
|
|
25
|
-
|
|
24
|
+
targetCtx: CanvasRenderingContext2D,
|
|
25
|
+
alpha = 255,
|
|
26
|
+
compOperation: GlobalCompositeOperation = 'source-over',
|
|
26
27
|
): void {
|
|
28
|
+
|
|
29
|
+
targetCtx.globalAlpha = alpha / 255
|
|
30
|
+
targetCtx.globalCompositeOperation = compOperation
|
|
31
|
+
|
|
27
32
|
for (let i = 0; i < lookup.length; i++) {
|
|
28
33
|
const tile = lookup[i]
|
|
29
34
|
|
|
@@ -33,8 +38,11 @@ export function makePaintBufferRenderer(
|
|
|
33
38
|
|
|
34
39
|
ctx.putImageData(tile.imageData, 0, 0)
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
targetCtx.drawImage(canvas, dx, dy)
|
|
37
42
|
}
|
|
38
43
|
}
|
|
44
|
+
|
|
45
|
+
targetCtx.globalAlpha = 1
|
|
46
|
+
targetCtx.globalCompositeOperation = 'source-over'
|
|
39
47
|
}
|
|
40
48
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MaskType, type PaintAlphaMask } from '../_types'
|
|
2
|
+
|
|
3
|
+
export function makeCirclePaintAlphaMask(size: number, fallOff: (d: number) => number = (d) => d): PaintAlphaMask {
|
|
4
|
+
const area = size * size
|
|
5
|
+
const data = new Uint8Array(area)
|
|
6
|
+
const radius = size / 2
|
|
7
|
+
const invR = 1 / radius
|
|
8
|
+
|
|
9
|
+
const centerOffset = -Math.ceil(radius - 0.5)
|
|
10
|
+
|
|
11
|
+
for (let y = 0; y < size; y++) {
|
|
12
|
+
const rowOffset = y * size
|
|
13
|
+
const dy = y - radius + 0.5
|
|
14
|
+
const dy2 = dy * dy
|
|
15
|
+
|
|
16
|
+
for (let x = 0; x < size; x++) {
|
|
17
|
+
const dx = x - radius + 0.5
|
|
18
|
+
const distSqr = dx * dx + dy2
|
|
19
|
+
|
|
20
|
+
if (distSqr <= (radius * radius)) {
|
|
21
|
+
const dist = Math.sqrt(distSqr) * invR
|
|
22
|
+
|
|
23
|
+
// Pass 1.0 at center, 0.0 at edge
|
|
24
|
+
const strength = fallOff(1 - dist)
|
|
25
|
+
if (strength > 0) {
|
|
26
|
+
const intensity = (strength * 255) | 0
|
|
27
|
+
data[rowOffset + x] = Math.max(0, Math.min(255, intensity))
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
type: MaskType.ALPHA,
|
|
35
|
+
data,
|
|
36
|
+
w: size,
|
|
37
|
+
h: size,
|
|
38
|
+
centerOffsetX: centerOffset,
|
|
39
|
+
centerOffsetY: centerOffset
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { MaskType, type PaintBinaryMask } from '../_types'
|
|
2
2
|
|
|
3
|
-
export function
|
|
3
|
+
export function makeCirclePaintBinaryMask(size: number): PaintBinaryMask {
|
|
4
4
|
const area = size * size
|
|
5
5
|
const data = new Uint8Array(area)
|
|
6
6
|
const radius = size / 2
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const centerOffset = -Math.ceil(radius - 0.5)
|
|
9
9
|
|
|
10
10
|
for (let y = 0; y < size; y++) {
|
|
11
11
|
for (let x = 0; x < size; x++) {
|
|
@@ -23,8 +23,7 @@ export function makeCircleBinaryMask(size: number): CircleBinaryMask {
|
|
|
23
23
|
data,
|
|
24
24
|
w: size,
|
|
25
25
|
h: size,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
minOffset,
|
|
26
|
+
centerOffsetX: centerOffset,
|
|
27
|
+
centerOffsetY: centerOffset,
|
|
29
28
|
}
|
|
30
29
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type AlphaMask, type BinaryMask, MaskType, type PaintAlphaMask, type PaintBinaryMask } from '../_types'
|
|
2
|
+
import { macro_halfAndFloor } from '../Internal/helpers'
|
|
3
|
+
|
|
4
|
+
export function makePaintBinaryMask(
|
|
5
|
+
mask: BinaryMask,
|
|
6
|
+
): PaintBinaryMask {
|
|
7
|
+
return {
|
|
8
|
+
type: MaskType.BINARY,
|
|
9
|
+
data: mask.data,
|
|
10
|
+
w: mask.w,
|
|
11
|
+
h: mask.h,
|
|
12
|
+
centerOffsetX: -macro_halfAndFloor(mask.w),
|
|
13
|
+
centerOffsetY: -macro_halfAndFloor(mask.h),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function makePaintAlphaMask(
|
|
18
|
+
mask: AlphaMask,
|
|
19
|
+
): PaintAlphaMask {
|
|
20
|
+
return {
|
|
21
|
+
type: MaskType.ALPHA,
|
|
22
|
+
data: mask.data,
|
|
23
|
+
w: mask.w,
|
|
24
|
+
h: mask.h,
|
|
25
|
+
centerOffsetX: -macro_halfAndFloor(mask.w),
|
|
26
|
+
centerOffsetY: -macro_halfAndFloor(mask.h),
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { MaskType, type PaintAlphaMask } from '../_types'
|
|
2
|
+
import { macro_halfAndFloor } from '../Internal/helpers'
|
|
3
|
+
|
|
4
|
+
export function makeRectFalloffPaintAlphaMask(
|
|
5
|
+
width: number,
|
|
6
|
+
height: number,
|
|
7
|
+
fallOff: (d: number) => number = (d) => d,
|
|
8
|
+
): PaintAlphaMask {
|
|
9
|
+
const fPx = Math.floor(width / 2)
|
|
10
|
+
const fPy = Math.floor(height / 2)
|
|
11
|
+
|
|
12
|
+
const invHalfW = 2 / width
|
|
13
|
+
const invHalfH = 2 / height
|
|
14
|
+
|
|
15
|
+
const offX = (width % 2 === 0) ? 0.5 : 0
|
|
16
|
+
const offY = (height % 2 === 0) ? 0.5 : 0
|
|
17
|
+
|
|
18
|
+
const area = width * height
|
|
19
|
+
const data = new Uint8Array(area)
|
|
20
|
+
|
|
21
|
+
for (let y = 0; y < height; y++) {
|
|
22
|
+
const dy = Math.abs((y - fPy) + offY) * invHalfH
|
|
23
|
+
const rowOffset = y * width
|
|
24
|
+
|
|
25
|
+
for (let x = 0; x < width; x++) {
|
|
26
|
+
const dx = Math.abs((x - fPx) + offX) * invHalfW
|
|
27
|
+
|
|
28
|
+
// Chebyshev distance (square/rect shape)
|
|
29
|
+
const dist = dx > dy ? dx : dy
|
|
30
|
+
// Pass 1.0 at center, 0.0 at edge
|
|
31
|
+
const strength = fallOff(1 - dist)
|
|
32
|
+
if (strength > 0) {
|
|
33
|
+
const intensity = (strength * 255) | 0
|
|
34
|
+
data[rowOffset + x] = Math.max(0, Math.min(255, intensity))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
type: MaskType.ALPHA,
|
|
41
|
+
data: data,
|
|
42
|
+
w: width,
|
|
43
|
+
h: height,
|
|
44
|
+
centerOffsetX: -macro_halfAndFloor(width),
|
|
45
|
+
centerOffsetY: -macro_halfAndFloor(height),
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ImageDataLike, IPixelData } from '../_types'
|
|
2
2
|
import { imageDataToUInt32Array } from '../ImageData/imageDataToUInt32Array'
|
|
3
3
|
|
|
4
|
-
export class PixelData<T extends ImageDataLike = ImageData> implements IPixelData {
|
|
4
|
+
export class PixelData<T extends ImageDataLike = ImageData> implements IPixelData<T> {
|
|
5
5
|
readonly data32: Uint32Array
|
|
6
6
|
readonly imageData: T
|
|
7
7
|
readonly width: number
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AlphaMask, type ApplyMaskToPixelDataOptions, type Color32, type
|
|
1
|
+
import { type AlphaMask, type ApplyMaskToPixelDataOptions, type Color32, type IPixelData32 } from '../_types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Directly applies a mask to a region of PixelData,
|
|
@@ -6,7 +6,7 @@ import { type AlphaMask, type ApplyMaskToPixelDataOptions, type Color32, type IP
|
|
|
6
6
|
* @returns true if any pixels were actually modified.
|
|
7
7
|
*/
|
|
8
8
|
export function applyAlphaMaskToPixelData(
|
|
9
|
-
dst:
|
|
9
|
+
dst: IPixelData32,
|
|
10
10
|
mask: AlphaMask,
|
|
11
11
|
opts: ApplyMaskToPixelDataOptions = {},
|
|
12
12
|
): boolean {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ApplyMaskToPixelDataOptions, type BinaryMask, type
|
|
1
|
+
import { type ApplyMaskToPixelDataOptions, type BinaryMask, type IPixelData32 } from '../_types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Directly applies a mask to a region of PixelData,
|
|
@@ -6,7 +6,7 @@ import { type ApplyMaskToPixelDataOptions, type BinaryMask, type IPixelData } fr
|
|
|
6
6
|
* @returns true if any pixels were actually modified.
|
|
7
7
|
*/
|
|
8
8
|
export function applyBinaryMaskToPixelData(
|
|
9
|
-
dst:
|
|
9
|
+
dst: IPixelData32,
|
|
10
10
|
mask: BinaryMask,
|
|
11
11
|
opts: ApplyMaskToPixelDataOptions = {},
|
|
12
12
|
): boolean {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Color32, type ColorBlendOptions, type
|
|
1
|
+
import { type Color32, type ColorBlendOptions, type IPixelData32 } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -6,7 +6,7 @@ import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
|
6
6
|
* @returns true if any pixels were actually modified.
|
|
7
7
|
*/
|
|
8
8
|
export function blendColorPixelData(
|
|
9
|
-
dst:
|
|
9
|
+
dst: IPixelData32,
|
|
10
10
|
color: Color32,
|
|
11
11
|
opts: ColorBlendOptions = {},
|
|
12
12
|
): boolean {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AlphaMask, Color32, ColorBlendMaskOptions,
|
|
1
|
+
import type { AlphaMask, Color32, ColorBlendMaskOptions, IPixelData32 } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -8,14 +8,14 @@ import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
|
8
8
|
* If the width (`w`) or height (`h`) are omitted from the options, they will safely
|
|
9
9
|
* default to the dimensions of the provided mask to prevent out-of-bounds memory access.
|
|
10
10
|
*
|
|
11
|
-
* @param dst - The destination {@link
|
|
11
|
+
* @param dst - The destination {@link IPixelData32} buffer to modify.
|
|
12
12
|
* @param color - The solid color to apply.
|
|
13
13
|
* @param mask - The mask defining the per-pixel opacity of the target area.
|
|
14
14
|
* @param opts - Configuration options including placement coordinates, bounds, global alpha, and mask offsets.
|
|
15
15
|
* @returns true if any pixels were actually modified.
|
|
16
16
|
*/
|
|
17
17
|
export function blendColorPixelDataAlphaMask(
|
|
18
|
-
dst:
|
|
18
|
+
dst: IPixelData32,
|
|
19
19
|
color: Color32,
|
|
20
20
|
mask: AlphaMask,
|
|
21
21
|
opts: ColorBlendMaskOptions = {},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BinaryMask, Color32, ColorBlendMaskOptions,
|
|
1
|
+
import type { BinaryMask, Color32, ColorBlendMaskOptions, IPixelData32 } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -8,14 +8,14 @@ import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
|
8
8
|
* If the width (`w`) or height (`h`) are omitted from the options, they will safely
|
|
9
9
|
* default to the dimensions of the provided mask to prevent out-of-bounds memory access.
|
|
10
10
|
*
|
|
11
|
-
* @param dst - The destination {@link
|
|
11
|
+
* @param dst - The destination {@link IPixelData32} buffer to modify.
|
|
12
12
|
* @param color - The solid color to apply.
|
|
13
13
|
* @param mask - The mask defining the per-pixel opacity of the target area.
|
|
14
14
|
* @param opts - Configuration options including placement coordinates, bounds, global alpha, and mask offsets.
|
|
15
15
|
* @returns true if any pixels were actually modified.
|
|
16
16
|
*/
|
|
17
17
|
export function blendColorPixelDataBinaryMask(
|
|
18
|
-
dst:
|
|
18
|
+
dst: IPixelData32,
|
|
19
19
|
color: Color32,
|
|
20
20
|
mask: BinaryMask,
|
|
21
21
|
opts: ColorBlendMaskOptions = {},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { BlendColor32, Color32,
|
|
1
|
+
import type { BlendColor32, Color32, IPixelData32 } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
4
|
export function blendPixel(
|
|
5
|
-
target:
|
|
5
|
+
target: IPixelData32,
|
|
6
6
|
x: number,
|
|
7
7
|
y: number,
|
|
8
8
|
color: Color32,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Color32, type
|
|
1
|
+
import { type Color32, type IPixelData32, type PixelBlendOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -16,8 +16,8 @@ import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
|
16
16
|
* });
|
|
17
17
|
*/
|
|
18
18
|
export function blendPixelData(
|
|
19
|
-
dst:
|
|
20
|
-
src:
|
|
19
|
+
dst: IPixelData32,
|
|
20
|
+
src: IPixelData32,
|
|
21
21
|
opts: PixelBlendOptions = {},
|
|
22
22
|
): boolean {
|
|
23
23
|
const {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type AlphaMask, type Color32, type
|
|
1
|
+
import { type AlphaMask, type Color32, type IPixelData32, type PixelBlendMaskOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
4
|
export function blendPixelDataAlphaMask(
|
|
5
|
-
dst:
|
|
6
|
-
src:
|
|
5
|
+
dst: IPixelData32,
|
|
6
|
+
src: IPixelData32,
|
|
7
7
|
alphaMask: AlphaMask,
|
|
8
8
|
opts: PixelBlendMaskOptions = {},
|
|
9
9
|
): boolean {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { BinaryMask, Color32,
|
|
1
|
+
import type { BinaryMask, Color32, IPixelData32, PixelBlendMaskOptions } from '../_types'
|
|
2
2
|
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
3
|
|
|
4
4
|
export function blendPixelDataBinaryMask(
|
|
5
|
-
dst:
|
|
6
|
-
src:
|
|
5
|
+
dst: IPixelData32,
|
|
6
|
+
src: IPixelData32,
|
|
7
7
|
binaryMask: BinaryMask,
|
|
8
8
|
opts: PixelBlendMaskOptions = {},
|
|
9
9
|
): boolean {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { BlendColor32,
|
|
1
|
+
import type { BlendColor32, IPixelData32 } from '../_types'
|
|
2
2
|
import { blendPixelData } from './blendPixelData'
|
|
3
|
-
import type { PaintBuffer } from '../
|
|
3
|
+
import type { PaintBuffer } from '../Paint/PaintBuffer'
|
|
4
4
|
|
|
5
5
|
const SCRATCH_OPTS = {
|
|
6
6
|
x: 0,
|
|
@@ -11,7 +11,7 @@ const SCRATCH_OPTS = {
|
|
|
11
11
|
|
|
12
12
|
export function blendPixelDataPaintBuffer(
|
|
13
13
|
paintBuffer: PaintBuffer,
|
|
14
|
-
target:
|
|
14
|
+
target: IPixelData32,
|
|
15
15
|
alpha = 255,
|
|
16
16
|
blendFn?: BlendColor32,
|
|
17
17
|
blendPixelDataFn = blendPixelData,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BinaryMaskRect, Color32,
|
|
1
|
+
import type { BinaryMaskRect, Color32, IPixelData32 } from '../_types'
|
|
2
2
|
import { fillPixelDataFast } from './fillPixelDataFast'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -6,7 +6,7 @@ import { fillPixelDataFast } from './fillPixelDataFast'
|
|
|
6
6
|
* Internally uses the optimized fillPixelData.
|
|
7
7
|
*/
|
|
8
8
|
export function clearPixelData(
|
|
9
|
-
dst:
|
|
9
|
+
dst: IPixelData32,
|
|
10
10
|
rect?: Partial<BinaryMaskRect>,
|
|
11
11
|
): void {
|
|
12
12
|
fillPixelDataFast(dst, 0 as Color32, rect)
|