pixel-data-js 0.26.0 → 0.28.0
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/README.md +12 -2
- package/dist/index.prod.cjs +2227 -1050
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +549 -424
- package/dist/index.prod.js +2171 -1028
- package/dist/index.prod.js.map +1 -1
- package/package.json +11 -11
- package/src/Algorithm/floodFillSelection.ts +8 -6
- package/src/Algorithm/forEachLinePoint.ts +6 -6
- package/src/{Internal/resample32.ts → Algorithm/resampleUint32Array.ts} +11 -21
- package/src/BlendModes/blend-modes-fast.ts +169 -0
- package/src/BlendModes/blend-modes-perfect.ts +207 -0
- package/src/BlendModes/blend-modes.ts +9 -0
- package/src/Canvas/CanvasFrameRenderer.ts +20 -28
- package/src/Canvas/CanvasPixelDataRenderer.ts +23 -0
- package/src/Canvas/PixelCanvas.ts +2 -7
- package/src/Canvas/ReusableCanvas.ts +4 -12
- package/src/Canvas/_canvas-types.ts +26 -0
- package/src/History/PixelAccumulator.ts +17 -17
- package/src/History/PixelEngineConfig.ts +3 -3
- package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +4 -3
- package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +4 -3
- package/src/History/PixelMutator/mutatorApplyMask.ts +4 -3
- package/src/History/PixelMutator/mutatorBlendAlphaMask.ts +6 -4
- package/src/History/PixelMutator/mutatorBlendBinaryMask.ts +6 -4
- package/src/History/PixelMutator/mutatorBlendColor.ts +2 -2
- package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +2 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +2 -1
- package/src/History/PixelMutator/mutatorBlendColorPaintMask.ts +3 -1
- package/src/History/PixelMutator/{mutatorBlendPaintRect.ts → mutatorBlendColorPaintRect.ts} +5 -5
- package/src/History/PixelMutator/mutatorBlendMask.ts +6 -4
- package/src/History/PixelMutator/mutatorBlendPixelData.ts +5 -4
- package/src/History/PixelMutator/mutatorClear.ts +4 -3
- package/src/History/PixelMutator/mutatorFill.ts +5 -4
- package/src/History/PixelMutator/mutatorFillBinaryMask.ts +2 -1
- package/src/History/PixelMutator/mutatorInvert.ts +2 -2
- package/src/History/PixelMutator.ts +2 -2
- package/src/History/PixelPatchTiles.ts +7 -7
- package/src/History/PixelWriter.ts +12 -63
- package/src/ImageData/ImageDataLike.ts +1 -1
- package/src/ImageData/_ImageData-types.ts +13 -0
- package/src/ImageData/copyImageData.ts +1 -1
- package/src/ImageData/extractImageDataBuffer.ts +3 -2
- package/src/ImageData/imageDataToUint32Array.ts +18 -0
- package/src/ImageData/resampleImageData.ts +3 -3
- package/src/ImageData/resizeImageData.ts +1 -1
- package/src/ImageData/serialization.ts +1 -1
- package/src/ImageData/uInt32ArrayToImageData.ts +1 -1
- package/src/ImageData/writeImageData.ts +2 -2
- package/src/ImageData/writeImageDataBuffer.ts +2 -2
- package/src/IndexedImage/IndexedImage.ts +56 -98
- package/src/IndexedImage/_indexedImage-types.ts +18 -0
- package/src/IndexedImage/getIndexedImageColorCounts.ts +3 -3
- package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
- package/src/IndexedImage/indexedImageToImageData.ts +4 -6
- package/src/IndexedImage/resampleIndexedImage.ts +7 -15
- package/src/Input/fileToImageData.ts +1 -1
- package/src/Internal/_errors.ts +2 -0
- package/src/Internal/macros.ts +14 -0
- package/src/Mask/AlphaMask.ts +1 -1
- package/src/Mask/BinaryMask/makeBinaryMaskFromAlphaMask.ts +23 -0
- package/src/Mask/BinaryMask/makeBinaryMaskOutline.ts +88 -0
- package/src/Mask/BinaryMask/makeCircleBinaryMaskOutline.ts +104 -0
- package/src/Mask/BinaryMask/makeRectBinaryMaskOutline.ts +34 -0
- package/src/Mask/BinaryMask.ts +1 -1
- package/src/Mask/_mask-types.ts +73 -0
- package/src/Mask/applyBinaryMaskToAlphaMask.ts +2 -1
- package/src/Mask/copyMask.ts +1 -1
- package/src/Mask/extractMask.ts +2 -1
- package/src/Mask/extractMaskBuffer.ts +1 -1
- package/src/Mask/mergeAlphaMasks.ts +6 -3
- package/src/Mask/mergeBinaryMasks.ts +2 -1
- package/src/Mask/setMaskData.ts +1 -1
- package/src/MaskRect/merge2BinaryMaskRects.ts +2 -2
- package/src/MaskRect/mergeBinaryMaskRects.ts +1 -1
- package/src/MaskRect/subtractBinaryMaskRects.ts +1 -1
- package/src/Paint/AlphaMaskPaintBuffer.ts +339 -0
- package/src/Paint/AlphaMaskPaintBufferCanvasRenderer.ts +78 -0
- package/src/Paint/BinaryMaskPaintBuffer.ts +254 -0
- package/src/Paint/BinaryMaskPaintBufferCanvasRenderer.ts +67 -0
- package/src/Paint/{PaintBuffer.ts → ColorPaintBuffer.ts} +148 -77
- package/src/Paint/{PaintBufferCanvasRenderer.ts → ColorPaintBufferCanvasRenderer.ts} +6 -5
- package/src/Paint/PaintCursorRenderer.ts +117 -0
- package/src/Paint/_paint-types.ts +22 -0
- package/src/Paint/eachTileInBounds.ts +45 -0
- package/src/Paint/makeCirclePaintMask.ts +74 -0
- package/src/Paint/makePaintMask.ts +5 -2
- package/src/Paint/makeRectFalloffPaintAlphaMask.ts +4 -2
- package/src/PixelData/PixelData.ts +15 -19
- package/src/PixelData/ReusablePixelData.ts +36 -0
- package/src/PixelData/_pixelData-types.ts +17 -0
- package/src/PixelData/applyAlphaMaskToPixelData.ts +80 -43
- package/src/PixelData/applyBinaryMaskToPixelData.ts +10 -8
- package/src/PixelData/applyMaskToPixelData.ts +4 -9
- package/src/PixelData/blendColorPixelData.ts +9 -8
- package/src/PixelData/blendColorPixelDataAlphaMask.ts +9 -7
- package/src/PixelData/blendColorPixelDataBinaryMask.ts +9 -7
- package/src/PixelData/blendColorPixelDataMask.ts +4 -2
- package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +4 -2
- package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +4 -2
- package/src/PixelData/blendColorPixelDataPaintMask.ts +5 -2
- package/src/PixelData/blendPixel.ts +6 -5
- package/src/PixelData/blendPixelData.ts +14 -13
- package/src/PixelData/blendPixelDataAlphaMask.ts +15 -13
- package/src/PixelData/blendPixelDataBinaryMask.ts +15 -13
- package/src/PixelData/blendPixelDataMask.ts +5 -3
- package/src/PixelData/blendPixelDataPaintBuffer.ts +5 -4
- package/src/PixelData/clearPixelDataFast.ts +4 -2
- package/src/PixelData/copyPixelData.ts +14 -0
- package/src/PixelData/extractPixelData.ts +8 -7
- package/src/PixelData/extractPixelDataBuffer.ts +9 -8
- package/src/PixelData/fillPixelData.ts +16 -14
- package/src/PixelData/fillPixelDataBinaryMask.ts +10 -8
- package/src/PixelData/fillPixelDataFast.ts +16 -14
- package/src/PixelData/invertPixelData.ts +9 -8
- package/src/PixelData/pixelDataToAlphaMask.ts +9 -8
- package/src/PixelData/reflectPixelData.ts +9 -9
- package/src/PixelData/resamplePixelData.ts +20 -9
- package/src/PixelData/rotatePixelData.ts +8 -7
- package/src/PixelData/uInt32ArrayToPixelData.ts +15 -0
- package/src/PixelData/writePaintBufferToPixelData.ts +5 -5
- package/src/PixelData/writePixelDataBuffer.ts +10 -9
- package/src/Rect/_rect-types.ts +7 -0
- package/src/Rect/getRectsBounds.ts +1 -1
- package/src/Rect/trimMaskRectBounds.ts +2 -1
- package/src/Rect/trimRectBounds.ts +1 -1
- package/src/Tile/MaskTile.ts +40 -0
- package/src/Tile/PixelTile.ts +23 -0
- package/src/{PixelTile/PixelTilePool.ts → Tile/TilePool.ts} +9 -9
- package/src/Tile/_tile-types.ts +33 -0
- package/src/_errors.ts +1 -0
- package/src/_types.ts +2 -118
- package/src/index.ts +47 -22
- package/src/ImageData/imageDataToUInt32Array.ts +0 -13
- package/src/Internal/helpers.ts +0 -5
- package/src/Paint/makeCirclePaintAlphaMask.ts +0 -41
- package/src/Paint/makeCirclePaintBinaryMask.ts +0 -29
- package/src/PixelTile/PixelTile.ts +0 -21
- /package/src/{Internal → Rect}/resolveClipping.ts +0 -0
|
@@ -1,78 +1,64 @@
|
|
|
1
|
-
import type { Color32
|
|
1
|
+
import type { Color32 } from '../_types'
|
|
2
2
|
import { forEachLinePoint } from '../Algorithm/forEachLinePoint'
|
|
3
|
+
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
4
|
+
import type { PixelAccumulator } from '../History/PixelAccumulator'
|
|
3
5
|
import type { PixelEngineConfig } from '../History/PixelEngineConfig'
|
|
4
|
-
import { _macro_paintRectCenterOffset } from '../Internal/
|
|
5
|
-
import
|
|
6
|
-
import type {
|
|
6
|
+
import { _macro_paintRectCenterOffset } from '../Internal/macros'
|
|
7
|
+
import { blendPixelData } from '../PixelData/blendPixelData'
|
|
8
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
7
9
|
import { trimRectBounds } from '../Rect/trimRectBounds'
|
|
10
|
+
import type { PixelTile } from '../Tile/_tile-types'
|
|
11
|
+
import type { TilePool } from '../Tile/TilePool'
|
|
12
|
+
import type { PaintAlphaMask, PaintBinaryMask } from './_paint-types'
|
|
13
|
+
import { eachTileInBounds } from './eachTileInBounds'
|
|
8
14
|
|
|
9
|
-
export class
|
|
15
|
+
export class ColorPaintBuffer {
|
|
10
16
|
readonly lookup: (PixelTile | undefined)[]
|
|
11
17
|
private readonly scratchBounds: Rect = { x: 0, y: 0, w: 0, h: 0 }
|
|
12
18
|
|
|
13
19
|
constructor(
|
|
14
20
|
readonly config: PixelEngineConfig,
|
|
15
|
-
readonly tilePool:
|
|
21
|
+
readonly tilePool: TilePool<PixelTile>,
|
|
22
|
+
private blendPixelDataFn = blendPixelData,
|
|
16
23
|
) {
|
|
17
24
|
this.lookup = []
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(
|
|
27
|
+
paintAlphaMask(
|
|
28
|
+
color: Color32,
|
|
29
|
+
brush: PaintAlphaMask,
|
|
30
|
+
x: number,
|
|
31
|
+
y: number,
|
|
32
|
+
): boolean
|
|
33
|
+
paintAlphaMask(
|
|
34
|
+
color: Color32,
|
|
35
|
+
brush: PaintAlphaMask,
|
|
36
|
+
startX: number,
|
|
37
|
+
startY: number,
|
|
38
|
+
endX: number,
|
|
39
|
+
endY: number,
|
|
40
|
+
): boolean
|
|
41
|
+
paintAlphaMask(
|
|
62
42
|
color: Color32,
|
|
63
43
|
brush: PaintAlphaMask,
|
|
64
44
|
x0: number,
|
|
65
45
|
y0: number,
|
|
66
|
-
x1: number,
|
|
67
|
-
y1: number,
|
|
46
|
+
x1: number = x0,
|
|
47
|
+
y1: number = y0,
|
|
68
48
|
): boolean {
|
|
69
49
|
const cA = color >>> 24
|
|
70
50
|
if (cA === 0) return false
|
|
71
51
|
|
|
72
|
-
const
|
|
52
|
+
const scratch = this.scratchBounds
|
|
53
|
+
const lookup = this.lookup
|
|
54
|
+
const tilePool = this.tilePool
|
|
55
|
+
const config = this.config
|
|
56
|
+
const tileShift = config.tileShift
|
|
57
|
+
const tileMask = config.tileMask
|
|
58
|
+
const target = config.target
|
|
59
|
+
|
|
73
60
|
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
74
61
|
const cRGB = color & 0x00ffffff
|
|
75
|
-
const scratch = this.scratchBounds
|
|
76
62
|
|
|
77
63
|
let changed = false
|
|
78
64
|
|
|
@@ -85,15 +71,15 @@ export class PaintBuffer {
|
|
|
85
71
|
topLeftY,
|
|
86
72
|
bW,
|
|
87
73
|
bH,
|
|
88
|
-
target.
|
|
89
|
-
target.
|
|
74
|
+
target.w,
|
|
75
|
+
target.h,
|
|
90
76
|
scratch,
|
|
91
77
|
)
|
|
92
78
|
|
|
93
79
|
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
94
80
|
|
|
95
|
-
|
|
96
|
-
const d32 = tile.
|
|
81
|
+
eachTileInBounds(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
82
|
+
const d32 = tile.data
|
|
97
83
|
let tileChanged = false
|
|
98
84
|
|
|
99
85
|
for (let i = 0; i < bH_t; i++) {
|
|
@@ -127,20 +113,40 @@ export class PaintBuffer {
|
|
|
127
113
|
return changed
|
|
128
114
|
}
|
|
129
115
|
|
|
130
|
-
|
|
116
|
+
paintBinaryMask(
|
|
117
|
+
color: Color32,
|
|
118
|
+
brush: PaintBinaryMask,
|
|
119
|
+
x: number,
|
|
120
|
+
y: number,
|
|
121
|
+
): boolean
|
|
122
|
+
paintBinaryMask(
|
|
123
|
+
color: Color32,
|
|
124
|
+
brush: PaintBinaryMask,
|
|
125
|
+
startX: number,
|
|
126
|
+
startY: number,
|
|
127
|
+
endX: number,
|
|
128
|
+
endY: number,
|
|
129
|
+
): boolean
|
|
130
|
+
paintBinaryMask(
|
|
131
131
|
color: Color32,
|
|
132
132
|
brush: PaintBinaryMask,
|
|
133
133
|
x0: number,
|
|
134
134
|
y0: number,
|
|
135
|
-
x1: number,
|
|
136
|
-
y1: number,
|
|
135
|
+
x1: number = x0,
|
|
136
|
+
y1: number = y0,
|
|
137
137
|
): boolean {
|
|
138
138
|
const alphaIsZero = (color >>> 24) === 0
|
|
139
139
|
if (alphaIsZero) return false
|
|
140
140
|
|
|
141
|
-
const { tileShift, tileMask, target } = this.config
|
|
142
|
-
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
143
141
|
const scratch = this.scratchBounds
|
|
142
|
+
const lookup = this.lookup
|
|
143
|
+
const tilePool = this.tilePool
|
|
144
|
+
const config = this.config
|
|
145
|
+
const tileShift = config.tileShift
|
|
146
|
+
const tileMask = config.tileMask
|
|
147
|
+
const target = config.target
|
|
148
|
+
|
|
149
|
+
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
144
150
|
let changed = false
|
|
145
151
|
|
|
146
152
|
forEachLinePoint(x0, y0, x1, y1, (px, py) => {
|
|
@@ -152,15 +158,15 @@ export class PaintBuffer {
|
|
|
152
158
|
topLeftY,
|
|
153
159
|
bW,
|
|
154
160
|
bH,
|
|
155
|
-
target.
|
|
156
|
-
target.
|
|
161
|
+
target.w,
|
|
162
|
+
target.h,
|
|
157
163
|
scratch,
|
|
158
164
|
)
|
|
159
165
|
|
|
160
166
|
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
161
167
|
|
|
162
|
-
|
|
163
|
-
const d32 = tile.
|
|
168
|
+
eachTileInBounds(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
169
|
+
const d32 = tile.data
|
|
164
170
|
let tileChanged = false
|
|
165
171
|
|
|
166
172
|
for (let i = 0; i < bH_t; i++) {
|
|
@@ -187,26 +193,44 @@ export class PaintBuffer {
|
|
|
187
193
|
return changed
|
|
188
194
|
}
|
|
189
195
|
|
|
190
|
-
|
|
196
|
+
paintRect(
|
|
197
|
+
color: Color32,
|
|
198
|
+
brushWidth: number,
|
|
199
|
+
brushHeight: number,
|
|
200
|
+
x: number,
|
|
201
|
+
y: number,
|
|
202
|
+
): boolean
|
|
203
|
+
paintRect(
|
|
204
|
+
color: Color32,
|
|
205
|
+
brushWidth: number,
|
|
206
|
+
brushHeight: number,
|
|
207
|
+
startX: number,
|
|
208
|
+
startY: number,
|
|
209
|
+
endX: number,
|
|
210
|
+
endY: number,
|
|
211
|
+
): boolean
|
|
212
|
+
paintRect(
|
|
191
213
|
color: Color32,
|
|
192
214
|
brushWidth: number,
|
|
193
215
|
brushHeight: number,
|
|
194
216
|
x0: number,
|
|
195
217
|
y0: number,
|
|
196
|
-
x1: number,
|
|
197
|
-
y1: number,
|
|
218
|
+
x1: number = x0,
|
|
219
|
+
y1: number = y0,
|
|
198
220
|
): boolean {
|
|
199
221
|
const alphaIsZero = (color >>> 24) === 0
|
|
200
222
|
if (alphaIsZero) return false
|
|
201
223
|
|
|
224
|
+
const scratch = this.scratchBounds
|
|
225
|
+
const lookup = this.lookup
|
|
226
|
+
const tilePool = this.tilePool
|
|
202
227
|
const config = this.config
|
|
203
228
|
const tileShift = config.tileShift
|
|
204
229
|
const tileMask = config.tileMask
|
|
205
230
|
const target = config.target
|
|
206
|
-
const scratch = this.scratchBounds
|
|
207
231
|
|
|
208
|
-
const centerOffsetX =
|
|
209
|
-
const centerOffsetY =
|
|
232
|
+
const centerOffsetX = _macro_paintRectCenterOffset(brushWidth)
|
|
233
|
+
const centerOffsetY = _macro_paintRectCenterOffset(brushHeight)
|
|
210
234
|
|
|
211
235
|
let changed = false
|
|
212
236
|
|
|
@@ -224,17 +248,15 @@ export class PaintBuffer {
|
|
|
224
248
|
topLeftY,
|
|
225
249
|
brushWidth,
|
|
226
250
|
brushHeight,
|
|
227
|
-
target.
|
|
228
|
-
target.
|
|
251
|
+
target.w,
|
|
252
|
+
target.h,
|
|
229
253
|
scratch,
|
|
230
254
|
)
|
|
231
255
|
|
|
232
256
|
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
233
257
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
(tile, bX, bY, bW_t, bH_t) => {
|
|
237
|
-
const d32 = tile.data32
|
|
258
|
+
eachTileInBounds(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
259
|
+
const d32 = tile.data
|
|
238
260
|
let tileChanged = false
|
|
239
261
|
|
|
240
262
|
for (let i = 0; i < bH_t; i++) {
|
|
@@ -263,6 +285,55 @@ export class PaintBuffer {
|
|
|
263
285
|
return changed
|
|
264
286
|
}
|
|
265
287
|
|
|
288
|
+
private opts = {
|
|
289
|
+
alpha: 255,
|
|
290
|
+
blendFn: sourceOverPerfect,
|
|
291
|
+
x: 0,
|
|
292
|
+
y: 0,
|
|
293
|
+
w: 0,
|
|
294
|
+
h: 0,
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
commit(
|
|
298
|
+
accumulator: PixelAccumulator,
|
|
299
|
+
alpha = 255,
|
|
300
|
+
blendFn = sourceOverPerfect,
|
|
301
|
+
) {
|
|
302
|
+
const tileShift = this.config.tileShift
|
|
303
|
+
const lookup = this.lookup
|
|
304
|
+
const opts = this.opts
|
|
305
|
+
|
|
306
|
+
const blendPixelDataFn = this.blendPixelDataFn
|
|
307
|
+
opts.alpha = alpha
|
|
308
|
+
opts.blendFn = blendFn
|
|
309
|
+
|
|
310
|
+
for (let i = 0; i < lookup.length; i++) {
|
|
311
|
+
const tile = lookup[i]
|
|
312
|
+
|
|
313
|
+
if (tile) {
|
|
314
|
+
const didChange = accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty)
|
|
315
|
+
|
|
316
|
+
const dx = tile.tx << tileShift
|
|
317
|
+
const dy = tile.ty << tileShift
|
|
318
|
+
|
|
319
|
+
opts.x = dx
|
|
320
|
+
opts.y = dy
|
|
321
|
+
opts.w = tile.w
|
|
322
|
+
opts.h = tile.h
|
|
323
|
+
|
|
324
|
+
didChange(
|
|
325
|
+
blendPixelDataFn(
|
|
326
|
+
this.config.target,
|
|
327
|
+
tile,
|
|
328
|
+
opts,
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.clear()
|
|
335
|
+
}
|
|
336
|
+
|
|
266
337
|
clear(): void {
|
|
267
338
|
this.tilePool.releaseTiles(this.lookup)
|
|
268
339
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { CANVAS_CTX_FAILED } from '
|
|
2
|
-
import type {
|
|
1
|
+
import { CANVAS_CTX_FAILED } from '../Internal/_errors'
|
|
2
|
+
import type { ColorPaintBuffer } from './ColorPaintBuffer'
|
|
3
3
|
|
|
4
|
-
export type
|
|
4
|
+
export type ColorPaintBufferCanvasRenderer = ReturnType<typeof makeColorPaintBufferCanvasRenderer>
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
*
|
|
8
|
+
* @param paintBuffer
|
|
8
9
|
* @param offscreenCanvasClass - @internal
|
|
9
10
|
*/
|
|
10
|
-
export function
|
|
11
|
-
paintBuffer:
|
|
11
|
+
export function makeColorPaintBufferCanvasRenderer(
|
|
12
|
+
paintBuffer: ColorPaintBuffer,
|
|
12
13
|
offscreenCanvasClass = OffscreenCanvas,
|
|
13
14
|
) {
|
|
14
15
|
const config = paintBuffer.config
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { type Color32 } from '../_types'
|
|
2
|
+
import type { CanvasContext, CanvasObjectFactory } from '../Canvas/_canvas-types'
|
|
3
|
+
import { packColor } from '../color'
|
|
4
|
+
import { CANVAS_CTX_FAILED } from '../Internal/_errors'
|
|
5
|
+
import { _macro_paintRectCenterOffset } from '../Internal/macros'
|
|
6
|
+
import { type BinaryMask, MaskType } from '../Mask/_mask-types'
|
|
7
|
+
import { makeBinaryMaskFromAlphaMask } from '../Mask/BinaryMask/makeBinaryMaskFromAlphaMask'
|
|
8
|
+
import { makeBinaryMaskOutline } from '../Mask/BinaryMask/makeBinaryMaskOutline'
|
|
9
|
+
import { makeCircleBinaryMaskOutline } from '../Mask/BinaryMask/makeCircleBinaryMaskOutline'
|
|
10
|
+
import { makeRectBinaryMaskOutline } from '../Mask/BinaryMask/makeRectBinaryMaskOutline'
|
|
11
|
+
import { fillPixelDataBinaryMask } from '../PixelData/fillPixelDataBinaryMask'
|
|
12
|
+
import { makeReusablePixelData } from '../PixelData/ReusablePixelData'
|
|
13
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
14
|
+
import { type PaintMask, PaintMaskOutline } from './_paint-types'
|
|
15
|
+
|
|
16
|
+
export type PaintCursorRenderer = ReturnType<typeof makePaintCursorRenderer>
|
|
17
|
+
|
|
18
|
+
export function makePaintCursorRenderer<T extends HTMLCanvasElement | OffscreenCanvas>(
|
|
19
|
+
factory: CanvasObjectFactory<T> = (w, h) => new OffscreenCanvas(w, h) as T,
|
|
20
|
+
) {
|
|
21
|
+
const canvas = factory(1, 1)
|
|
22
|
+
const ctx = canvas.getContext('2d')! as CanvasContext<T>
|
|
23
|
+
if (!ctx) throw new Error(CANVAS_CTX_FAILED)
|
|
24
|
+
ctx.imageSmoothingEnabled = false
|
|
25
|
+
|
|
26
|
+
const getPixelData = makeReusablePixelData()
|
|
27
|
+
|
|
28
|
+
let _color = packColor(0, 255, 255, 255)
|
|
29
|
+
let _scale = 1
|
|
30
|
+
|
|
31
|
+
let currentMask: PaintMask = {
|
|
32
|
+
type: MaskType.BINARY,
|
|
33
|
+
outlineType: PaintMaskOutline.RECT,
|
|
34
|
+
w: 1,
|
|
35
|
+
h: 1,
|
|
36
|
+
centerOffsetX: _macro_paintRectCenterOffset(10),
|
|
37
|
+
centerOffsetY: _macro_paintRectCenterOffset(10),
|
|
38
|
+
} as PaintMask
|
|
39
|
+
|
|
40
|
+
let outline: BinaryMask
|
|
41
|
+
|
|
42
|
+
function update(paintMask?: PaintMask, scale?: number, color?: Color32, alphaThreshold = 127) {
|
|
43
|
+
currentMask = paintMask ?? currentMask
|
|
44
|
+
|
|
45
|
+
_scale = scale ?? _scale
|
|
46
|
+
_color = color ?? _color
|
|
47
|
+
|
|
48
|
+
canvas.width = currentMask.w * _scale + 2 * _scale
|
|
49
|
+
canvas.height = currentMask.h * _scale + 2 * _scale
|
|
50
|
+
|
|
51
|
+
if (currentMask.type === MaskType.BINARY) {
|
|
52
|
+
if (currentMask.outlineType === PaintMaskOutline.CIRCLE) {
|
|
53
|
+
outline = makeCircleBinaryMaskOutline(currentMask.w, _scale)
|
|
54
|
+
} else if (currentMask.outlineType === PaintMaskOutline.RECT) {
|
|
55
|
+
outline = makeRectBinaryMaskOutline(currentMask.w, currentMask.h, _scale)
|
|
56
|
+
} else if (currentMask.outlineType === PaintMaskOutline.MASKED) {
|
|
57
|
+
outline = makeBinaryMaskOutline(currentMask, _scale)
|
|
58
|
+
}
|
|
59
|
+
} else if (currentMask.type === MaskType.ALPHA) {
|
|
60
|
+
const mask = makeBinaryMaskFromAlphaMask(currentMask, alphaThreshold)
|
|
61
|
+
outline = makeBinaryMaskOutline(mask, _scale)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const pixelData = getPixelData(outline.w, outline.h)
|
|
65
|
+
fillPixelDataBinaryMask(pixelData, _color, outline)
|
|
66
|
+
ctx.putImageData(pixelData.imageData, 0, 0)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const boundsScratch = { x: 0, y: 0, w: 0, h: 0 }
|
|
70
|
+
|
|
71
|
+
function getBounds(centerX: number, centerY: number): Rect {
|
|
72
|
+
boundsScratch.x = centerX + currentMask.centerOffsetX
|
|
73
|
+
boundsScratch.y = centerY + currentMask.centerOffsetY
|
|
74
|
+
boundsScratch.w = currentMask.w
|
|
75
|
+
boundsScratch.h = currentMask.h
|
|
76
|
+
|
|
77
|
+
return boundsScratch
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const boundsScaledScratch = { x: 0, y: 0, w: 0, h: 0 }
|
|
81
|
+
|
|
82
|
+
function getOutlineBoundsScaled(centerX: number, centerY: number): Rect {
|
|
83
|
+
boundsScaledScratch.x = centerX * _scale + currentMask.centerOffsetX * _scale - 1
|
|
84
|
+
boundsScaledScratch.y = centerY * _scale + currentMask.centerOffsetY * _scale - 1
|
|
85
|
+
boundsScaledScratch.w = currentMask.w * _scale
|
|
86
|
+
boundsScaledScratch.h = currentMask.h * _scale
|
|
87
|
+
|
|
88
|
+
return boundsScaledScratch
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function draw(
|
|
92
|
+
drawCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
|
93
|
+
centerX: number,
|
|
94
|
+
centerY: number,
|
|
95
|
+
) {
|
|
96
|
+
const dx = centerX * _scale + currentMask.centerOffsetX * _scale - 1
|
|
97
|
+
const dy = centerY * _scale + currentMask.centerOffsetY * _scale - 1
|
|
98
|
+
|
|
99
|
+
drawCtx.drawImage(canvas, Math.floor(dx), Math.floor(dy))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getSettings() {
|
|
103
|
+
return {
|
|
104
|
+
color: _color,
|
|
105
|
+
scale: _scale,
|
|
106
|
+
currentMask,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
update,
|
|
112
|
+
getBounds,
|
|
113
|
+
getBoundsScaled: getOutlineBoundsScaled,
|
|
114
|
+
draw,
|
|
115
|
+
getSettings,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AlphaMask, BinaryMask } from '../Mask/_mask-types'
|
|
2
|
+
|
|
3
|
+
export enum PaintMaskOutline {
|
|
4
|
+
MASKED,
|
|
5
|
+
CIRCLE,
|
|
6
|
+
RECT,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface BasePaintMask<T extends PaintMaskOutline = PaintMaskOutline> {
|
|
10
|
+
readonly outlineType: T
|
|
11
|
+
readonly centerOffsetX: number
|
|
12
|
+
readonly centerOffsetY: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PaintAlphaMask<T extends PaintMaskOutline = PaintMaskOutline> extends BasePaintMask<T>, AlphaMask {
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PaintBinaryMask<T extends PaintMaskOutline = PaintMaskOutline> extends BasePaintMask<T>, BinaryMask {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type PaintMask = PaintAlphaMask<any> | PaintBinaryMask<any>
|
|
22
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { PixelEngineConfig } from '../History/PixelEngineConfig'
|
|
2
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
3
|
+
import type { Tile } from '../Tile/_tile-types'
|
|
4
|
+
import type { TilePool } from '../Tile/TilePool'
|
|
5
|
+
|
|
6
|
+
export function eachTileInBounds<T extends Tile>(
|
|
7
|
+
config: PixelEngineConfig,
|
|
8
|
+
lookup: (T | undefined)[],
|
|
9
|
+
tilePool: TilePool<T>,
|
|
10
|
+
bounds: Rect,
|
|
11
|
+
callback: (tile: T, bX: number, bY: number, bW: number, bH: number) => void,
|
|
12
|
+
): void {
|
|
13
|
+
const { tileShift, targetColumns, targetRows, tileSize } = config
|
|
14
|
+
|
|
15
|
+
const x1 = Math.max(0, bounds.x >> tileShift)
|
|
16
|
+
const y1 = Math.max(0, bounds.y >> tileShift)
|
|
17
|
+
const x2 = Math.min(targetColumns - 1, (bounds.x + bounds.w - 1) >> tileShift)
|
|
18
|
+
const y2 = Math.min(targetRows - 1, (bounds.y + bounds.h - 1) >> tileShift)
|
|
19
|
+
|
|
20
|
+
if (x1 > x2 || y1 > y2) return
|
|
21
|
+
|
|
22
|
+
for (let ty = y1; ty <= y2; ty++) {
|
|
23
|
+
const rowOffset = ty * targetColumns
|
|
24
|
+
const tileTop = ty << tileShift
|
|
25
|
+
|
|
26
|
+
for (let tx = x1; tx <= x2; tx++) {
|
|
27
|
+
const id = rowOffset + tx
|
|
28
|
+
const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty))
|
|
29
|
+
const tileLeft = tx << tileShift
|
|
30
|
+
|
|
31
|
+
const startX = bounds.x > tileLeft ? bounds.x : tileLeft
|
|
32
|
+
const startY = bounds.y > tileTop ? bounds.y : tileTop
|
|
33
|
+
|
|
34
|
+
const maskEndX = bounds.x + bounds.w
|
|
35
|
+
const tileEndX = tileLeft + tileSize
|
|
36
|
+
const endX = maskEndX < tileEndX ? maskEndX : tileEndX
|
|
37
|
+
|
|
38
|
+
const maskEndY = bounds.y + bounds.h
|
|
39
|
+
const tileEndY = tileTop + tileSize
|
|
40
|
+
const endY = maskEndY < tileEndY ? maskEndY : tileEndY
|
|
41
|
+
|
|
42
|
+
callback(tile, startX, startY, endX - startX, endY - startY)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { _macro_paintCircleCenterOffset } from '../Internal/macros'
|
|
2
|
+
import { MaskType } from '../Mask/_mask-types'
|
|
3
|
+
import { type PaintAlphaMask, type PaintBinaryMask, PaintMaskOutline } from './_paint-types'
|
|
4
|
+
|
|
5
|
+
export function makeCirclePaintAlphaMask(size: number, fallOff: (d: number) => number = (d) => d): PaintAlphaMask {
|
|
6
|
+
const area = size * size
|
|
7
|
+
const data = new Uint8Array(area)
|
|
8
|
+
const radius = size / 2
|
|
9
|
+
const invR = 1 / radius
|
|
10
|
+
|
|
11
|
+
const centerOffset = _macro_paintCircleCenterOffset(radius)
|
|
12
|
+
|
|
13
|
+
for (let y = 0; y < size; y++) {
|
|
14
|
+
const rowOffset = y * size
|
|
15
|
+
const dy = y - radius + 0.5
|
|
16
|
+
const dy2 = dy * dy
|
|
17
|
+
|
|
18
|
+
for (let x = 0; x < size; x++) {
|
|
19
|
+
const dx = x - radius + 0.5
|
|
20
|
+
const distSqr = dx * dx + dy2
|
|
21
|
+
|
|
22
|
+
if (distSqr <= (radius * radius)) {
|
|
23
|
+
const dist = Math.sqrt(distSqr) * invR
|
|
24
|
+
|
|
25
|
+
// Pass 1.0 at center, 0.0 at edge
|
|
26
|
+
const strength = fallOff(1 - dist)
|
|
27
|
+
if (strength > 0) {
|
|
28
|
+
const intensity = (strength * 255) | 0
|
|
29
|
+
data[rowOffset + x] = Math.max(0, Math.min(255, intensity))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
type: MaskType.ALPHA,
|
|
37
|
+
outlineType: PaintMaskOutline.CIRCLE,
|
|
38
|
+
data,
|
|
39
|
+
w: size,
|
|
40
|
+
h: size,
|
|
41
|
+
centerOffsetX: centerOffset,
|
|
42
|
+
centerOffsetY: centerOffset,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function makeCirclePaintBinaryMask(size: number): PaintBinaryMask {
|
|
47
|
+
const area = size * size
|
|
48
|
+
const data = new Uint8Array(area)
|
|
49
|
+
const radius = size / 2
|
|
50
|
+
const r2 = radius * radius
|
|
51
|
+
|
|
52
|
+
for (let y = 0; y < size; y++) {
|
|
53
|
+
for (let x = 0; x < size; x++) {
|
|
54
|
+
const dx = x - radius + 0.5
|
|
55
|
+
const dy = y - radius + 0.5
|
|
56
|
+
const distSqr = dx * dx + dy * dy
|
|
57
|
+
if (distSqr <= r2) {
|
|
58
|
+
data[y * size + x] = 1
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const centerOffset = _macro_paintCircleCenterOffset(radius)
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
type: MaskType.BINARY,
|
|
67
|
+
outlineType: PaintMaskOutline.CIRCLE,
|
|
68
|
+
w: size,
|
|
69
|
+
h: size,
|
|
70
|
+
data,
|
|
71
|
+
centerOffsetX: centerOffset,
|
|
72
|
+
centerOffsetY: centerOffset,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { _macro_halfAndFloor } from '../Internal/macros'
|
|
2
|
+
import { type AlphaMask, type BinaryMask, MaskType } from '../Mask/_mask-types'
|
|
3
|
+
import { type PaintAlphaMask, type PaintBinaryMask, PaintMaskOutline } from './_paint-types'
|
|
3
4
|
|
|
4
5
|
export function makePaintBinaryMask(
|
|
5
6
|
mask: BinaryMask,
|
|
6
7
|
): PaintBinaryMask {
|
|
7
8
|
return {
|
|
8
9
|
type: MaskType.BINARY,
|
|
10
|
+
outlineType: PaintMaskOutline.MASKED,
|
|
9
11
|
data: mask.data,
|
|
10
12
|
w: mask.w,
|
|
11
13
|
h: mask.h,
|
|
@@ -19,6 +21,7 @@ export function makePaintAlphaMask(
|
|
|
19
21
|
): PaintAlphaMask {
|
|
20
22
|
return {
|
|
21
23
|
type: MaskType.ALPHA,
|
|
24
|
+
outlineType: PaintMaskOutline.MASKED,
|
|
22
25
|
data: mask.data,
|
|
23
26
|
w: mask.w,
|
|
24
27
|
h: mask.h,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { _macro_halfAndFloor } from '../Internal/macros'
|
|
2
|
+
import { MaskType } from '../Mask/_mask-types'
|
|
3
|
+
import { type PaintAlphaMask, PaintMaskOutline } from './_paint-types'
|
|
3
4
|
|
|
4
5
|
export function makeRectFalloffPaintAlphaMask(
|
|
5
6
|
width: number,
|
|
@@ -38,6 +39,7 @@ export function makeRectFalloffPaintAlphaMask(
|
|
|
38
39
|
|
|
39
40
|
return {
|
|
40
41
|
type: MaskType.ALPHA,
|
|
42
|
+
outlineType: PaintMaskOutline.RECT,
|
|
41
43
|
data: data,
|
|
42
44
|
w: width,
|
|
43
45
|
h: height,
|