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
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { type BinaryMask, MaskType } from '../_mask-types'
|
|
2
|
+
|
|
3
|
+
export function makeCircleBinaryMaskOutline(size: number, scale: number): BinaryMask {
|
|
4
|
+
const outSize = size * scale + 2
|
|
5
|
+
const outArea = outSize * outSize
|
|
6
|
+
const data = new Uint8Array(outArea)
|
|
7
|
+
|
|
8
|
+
const radius = size / 2
|
|
9
|
+
const r2 = radius * radius
|
|
10
|
+
|
|
11
|
+
let prevMinX = -1
|
|
12
|
+
let prevMaxX = -1
|
|
13
|
+
|
|
14
|
+
let currMinX = -1
|
|
15
|
+
let currMaxX = -1
|
|
16
|
+
|
|
17
|
+
const initialDy = 0 - radius + 0.5
|
|
18
|
+
const initialDy2 = initialDy * initialDy
|
|
19
|
+
|
|
20
|
+
if (initialDy2 <= r2) {
|
|
21
|
+
const dx = Math.sqrt(r2 - initialDy2)
|
|
22
|
+
currMinX = Math.ceil(radius - 0.5 - dx)
|
|
23
|
+
currMaxX = Math.floor(radius - 0.5 + dx)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (let iy = 0; iy < size; iy++) {
|
|
27
|
+
let nextMinX = -1
|
|
28
|
+
let nextMaxX = -1
|
|
29
|
+
|
|
30
|
+
if (iy + 1 < size) {
|
|
31
|
+
const ny = (iy + 1) - radius + 0.5
|
|
32
|
+
const ny2 = ny * ny
|
|
33
|
+
|
|
34
|
+
if (ny2 <= r2) {
|
|
35
|
+
const dx = Math.sqrt(r2 - ny2)
|
|
36
|
+
nextMinX = Math.ceil(radius - 0.5 - dx)
|
|
37
|
+
nextMaxX = Math.floor(radius - 0.5 + dx)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (currMinX !== -1) {
|
|
42
|
+
for (let ix = currMinX; ix <= currMaxX; ix++) {
|
|
43
|
+
// Offset by 1 to leave room for the top/left outline edges
|
|
44
|
+
const sx = ix * scale + 1
|
|
45
|
+
const sy = iy * scale + 1
|
|
46
|
+
|
|
47
|
+
const isTop = prevMinX === -1 || ix < prevMinX || ix > prevMaxX
|
|
48
|
+
const isBottom = nextMinX === -1 || ix < nextMinX || ix > nextMaxX
|
|
49
|
+
const isLeft = ix === currMinX
|
|
50
|
+
const isRight = ix === currMaxX
|
|
51
|
+
|
|
52
|
+
if (isTop) {
|
|
53
|
+
const leftOut = prevMinX === -1 || (ix - 1) < prevMinX || (ix - 1) > prevMaxX
|
|
54
|
+
const rightOut = prevMinX === -1 || (ix + 1) < prevMinX || (ix + 1) > prevMaxX
|
|
55
|
+
const startX = leftOut ? sx - 1 : sx
|
|
56
|
+
const endX = rightOut ? sx + scale : sx + scale - 1
|
|
57
|
+
|
|
58
|
+
for (let x = startX; x <= endX; x++) {
|
|
59
|
+
const index = (sy - 1) * outSize + x
|
|
60
|
+
data[index] = 1
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (isBottom) {
|
|
65
|
+
const leftOut = nextMinX === -1 || (ix - 1) < nextMinX || (ix - 1) > nextMaxX
|
|
66
|
+
const rightOut = nextMinX === -1 || (ix + 1) < nextMinX || (ix + 1) > nextMaxX
|
|
67
|
+
const startX = leftOut ? sx - 1 : sx
|
|
68
|
+
const endX = rightOut ? sx + scale : sx + scale - 1
|
|
69
|
+
|
|
70
|
+
for (let x = startX; x <= endX; x++) {
|
|
71
|
+
const index = (sy + scale) * outSize + x
|
|
72
|
+
data[index] = 1
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (isLeft) {
|
|
77
|
+
for (let y = sy; y < sy + scale; y++) {
|
|
78
|
+
const index = y * outSize + (sx - 1)
|
|
79
|
+
data[index] = 1
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isRight) {
|
|
84
|
+
for (let y = sy; y < sy + scale; y++) {
|
|
85
|
+
const index = y * outSize + (sx + scale)
|
|
86
|
+
data[index] = 1
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
prevMinX = currMinX
|
|
93
|
+
prevMaxX = currMaxX
|
|
94
|
+
currMinX = nextMinX
|
|
95
|
+
currMaxX = nextMaxX
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
type: MaskType.BINARY,
|
|
100
|
+
w: outSize,
|
|
101
|
+
h: outSize,
|
|
102
|
+
data,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type BinaryMask, MaskType } from '../_mask-types'
|
|
2
|
+
|
|
3
|
+
export function makeRectBinaryMaskOutline(
|
|
4
|
+
w: number,
|
|
5
|
+
h: number,
|
|
6
|
+
scale = 1,
|
|
7
|
+
): BinaryMask {
|
|
8
|
+
const rw = w * scale
|
|
9
|
+
const rh = h * scale
|
|
10
|
+
|
|
11
|
+
const outW = rw + 2
|
|
12
|
+
const outH = rh + 2
|
|
13
|
+
const outData = new Uint8Array(outW * outH)
|
|
14
|
+
|
|
15
|
+
// Top edge
|
|
16
|
+
outData.fill(1, 0, outW)
|
|
17
|
+
|
|
18
|
+
// Bottom edge
|
|
19
|
+
outData.fill(1, (outH - 1) * outW, outH * outW)
|
|
20
|
+
|
|
21
|
+
// Left and Right edges
|
|
22
|
+
for (let iy = 1; iy < outH - 1; iy++) {
|
|
23
|
+
const rowStart = iy * outW
|
|
24
|
+
outData[rowStart] = 1
|
|
25
|
+
outData[rowStart + outW - 1] = 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
type: MaskType.BINARY,
|
|
30
|
+
w: outW,
|
|
31
|
+
h: outH,
|
|
32
|
+
data: outData,
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/Mask/BinaryMask.ts
CHANGED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Defines how mask values should be interpreted during a draw operation.
|
|
5
|
+
*/
|
|
6
|
+
export enum MaskType {
|
|
7
|
+
/**
|
|
8
|
+
* Values are treated as alpha weights.
|
|
9
|
+
* 0 is skipped, values > 0 are processed.
|
|
10
|
+
*/
|
|
11
|
+
ALPHA,
|
|
12
|
+
/**
|
|
13
|
+
* Values are treated as on/off.
|
|
14
|
+
* 0 is fully transparent (skipped), any other value is fully opaque.
|
|
15
|
+
*/
|
|
16
|
+
BINARY
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface BaseMask {
|
|
20
|
+
readonly type: MaskType
|
|
21
|
+
readonly data: Uint8Array
|
|
22
|
+
readonly w: number
|
|
23
|
+
readonly h: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type Mask = BinaryMask | AlphaMask
|
|
27
|
+
|
|
28
|
+
/** Strictly 0 or 1 */
|
|
29
|
+
export interface BinaryMask extends BaseMask {
|
|
30
|
+
readonly type: MaskType.BINARY
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Strictly 0-255 */
|
|
34
|
+
export interface AlphaMask extends BaseMask {
|
|
35
|
+
readonly type: MaskType.ALPHA
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MutableMask<T extends MaskType> {
|
|
39
|
+
readonly type: T
|
|
40
|
+
data: Uint8Array
|
|
41
|
+
w: number
|
|
42
|
+
h: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface MutableAlphaMask extends MutableMask<MaskType.ALPHA> {
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface MutableBinaryMask extends MutableMask<MaskType.BINARY> {
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type MaskRect<T extends MaskType> = Rect & {
|
|
52
|
+
type: T
|
|
53
|
+
data: Uint8Array
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type BinaryMaskRect = MaskRect<MaskType.BINARY>
|
|
57
|
+
|
|
58
|
+
export type AlphaMaskRect = MaskRect<MaskType.ALPHA>
|
|
59
|
+
|
|
60
|
+
export type NullableBinaryMaskRect = Rect & ({
|
|
61
|
+
type: MaskType.BINARY
|
|
62
|
+
data: Uint8Array
|
|
63
|
+
} | {
|
|
64
|
+
type?: null
|
|
65
|
+
data?: null
|
|
66
|
+
})
|
|
67
|
+
export type NullableMaskRect = Rect & ({
|
|
68
|
+
type: MaskType
|
|
69
|
+
data: Uint8Array
|
|
70
|
+
} | {
|
|
71
|
+
type?: null
|
|
72
|
+
data?: null
|
|
73
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApplyMaskToPixelDataOptions } from '../_types'
|
|
2
|
+
import type { AlphaMask, BinaryMask } from './_mask-types'
|
|
2
3
|
|
|
3
4
|
export function applyBinaryMaskToAlphaMask(
|
|
4
5
|
alphaMaskDst: AlphaMask,
|
package/src/Mask/copyMask.ts
CHANGED
package/src/Mask/extractMask.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { MergeAlphaMasksOptions } from '../_types'
|
|
2
|
+
import type { AlphaMask } from './_mask-types'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Merges 2 alpha masks values are 0-255
|
|
@@ -58,7 +59,8 @@ export function mergeAlphaMasks(
|
|
|
58
59
|
} else if (globalAlpha === 255) {
|
|
59
60
|
weight = effectiveM
|
|
60
61
|
} else {
|
|
61
|
-
|
|
62
|
+
const t = effectiveM * globalAlpha + 128
|
|
63
|
+
weight = (t + (t >> 8)) >> 8
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
if (weight !== 255) {
|
|
@@ -70,7 +72,8 @@ export function mergeAlphaMasks(
|
|
|
70
72
|
if (da === 255) {
|
|
71
73
|
dstData[dIdx] = weight
|
|
72
74
|
} else if (da !== 0) {
|
|
73
|
-
|
|
75
|
+
const t = da * weight + 128
|
|
76
|
+
dstData[dIdx] = (t + (t >> 8)) >> 8
|
|
74
77
|
}
|
|
75
78
|
}
|
|
76
79
|
}
|
package/src/Mask/setMaskData.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MaskType, type NullableBinaryMaskRect } from '../
|
|
1
|
+
import { MaskType, type NullableBinaryMaskRect } from '../Mask/_mask-types'
|
|
2
2
|
import { getRectsBounds } from '../Rect/getRectsBounds'
|
|
3
3
|
|
|
4
4
|
export function merge2BinaryMaskRects(
|
|
@@ -34,7 +34,7 @@ export function merge2BinaryMaskRects(
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
const maskData = new Uint8Array(bounds.w * bounds.h)
|
|
38
38
|
|
|
39
39
|
// --- Write A's contribution ---
|
|
40
40
|
const aOffY = a.y - bounds.y
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NullableBinaryMaskRect } from '../
|
|
1
|
+
import type { NullableBinaryMaskRect } from '../Mask/_mask-types'
|
|
2
2
|
import { merge2BinaryMaskRects } from './merge2BinaryMaskRects'
|
|
3
3
|
|
|
4
4
|
export function mergeBinaryMaskRects(current: NullableBinaryMaskRect[], adding: NullableBinaryMaskRect[]): NullableBinaryMaskRect[] {
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { type Color32 } from '../_types'
|
|
2
|
+
import { forEachLinePoint } from '../Algorithm/forEachLinePoint'
|
|
3
|
+
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
4
|
+
import type { PixelAccumulator } from '../History/PixelAccumulator'
|
|
5
|
+
import type { PixelEngineConfig } from '../History/PixelEngineConfig'
|
|
6
|
+
import { _macro_paintRectCenterOffset } from '../Internal/macros'
|
|
7
|
+
import { blendColorPixelDataAlphaMask } from '../PixelData/blendColorPixelDataAlphaMask'
|
|
8
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
9
|
+
import { trimRectBounds } from '../Rect/trimRectBounds'
|
|
10
|
+
import type { AlphaMaskTile } from '../Tile/_tile-types'
|
|
11
|
+
import type { TilePool } from '../Tile/TilePool'
|
|
12
|
+
import type { PaintAlphaMask, PaintBinaryMask } from './_paint-types'
|
|
13
|
+
import { eachTileInBounds } from './eachTileInBounds'
|
|
14
|
+
|
|
15
|
+
export class AlphaMaskPaintBuffer {
|
|
16
|
+
readonly lookup: (AlphaMaskTile | undefined)[]
|
|
17
|
+
private readonly scratchBounds: Rect = { x: 0, y: 0, w: 0, h: 0 }
|
|
18
|
+
|
|
19
|
+
private blendColorPixelDataAlphaMaskFn = blendColorPixelDataAlphaMask
|
|
20
|
+
private forEachLinePointFn = forEachLinePoint
|
|
21
|
+
private trimRectBoundsFn = trimRectBounds
|
|
22
|
+
private eachTileInBoundsFn = eachTileInBounds
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
readonly config: PixelEngineConfig,
|
|
26
|
+
readonly tilePool: TilePool<AlphaMaskTile>,
|
|
27
|
+
) {
|
|
28
|
+
this.lookup = []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
paintAlphaMask(
|
|
32
|
+
brush: PaintAlphaMask,
|
|
33
|
+
x: number,
|
|
34
|
+
y: number,
|
|
35
|
+
): boolean
|
|
36
|
+
paintAlphaMask(
|
|
37
|
+
brush: PaintAlphaMask,
|
|
38
|
+
startX: number,
|
|
39
|
+
startY: number,
|
|
40
|
+
endX: number,
|
|
41
|
+
endY: number,
|
|
42
|
+
): boolean
|
|
43
|
+
paintAlphaMask(
|
|
44
|
+
brush: PaintAlphaMask,
|
|
45
|
+
x0: number,
|
|
46
|
+
y0: number,
|
|
47
|
+
x1: number = x0,
|
|
48
|
+
y1: number = y0,
|
|
49
|
+
): boolean {
|
|
50
|
+
|
|
51
|
+
const scratch = this.scratchBounds
|
|
52
|
+
const lookup = this.lookup
|
|
53
|
+
const tilePool = this.tilePool
|
|
54
|
+
const config = this.config
|
|
55
|
+
const tileShift = config.tileShift
|
|
56
|
+
const tileMask = config.tileMask
|
|
57
|
+
const target = config.target
|
|
58
|
+
|
|
59
|
+
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
60
|
+
|
|
61
|
+
let changed = false
|
|
62
|
+
|
|
63
|
+
const eachTileInBoundsFn = this.eachTileInBoundsFn
|
|
64
|
+
const trimRectBoundsFn = this.trimRectBoundsFn
|
|
65
|
+
|
|
66
|
+
this.forEachLinePointFn(x0, y0, x1, y1, (px, py) => {
|
|
67
|
+
|
|
68
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
69
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
70
|
+
trimRectBoundsFn(
|
|
71
|
+
topLeftX,
|
|
72
|
+
topLeftY,
|
|
73
|
+
bW,
|
|
74
|
+
bH,
|
|
75
|
+
target.w,
|
|
76
|
+
target.h,
|
|
77
|
+
scratch,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
81
|
+
|
|
82
|
+
eachTileInBoundsFn(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
83
|
+
const data = tile.data
|
|
84
|
+
let tileChanged = false
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < bH_t; i++) {
|
|
87
|
+
const canvasY = bY + i
|
|
88
|
+
const bOff = (canvasY - topLeftY) * bW
|
|
89
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
90
|
+
const dS = tOff + (bX & tileMask)
|
|
91
|
+
|
|
92
|
+
for (let j = 0; j < bW_t; j++) {
|
|
93
|
+
const canvasX = bX + j
|
|
94
|
+
const brushA = bD[bOff + (canvasX - topLeftX)]
|
|
95
|
+
if (brushA === 0) continue
|
|
96
|
+
const idx = dS + j
|
|
97
|
+
|
|
98
|
+
// Only overwrite if the brush stroke is stronger than the existing mask pixel
|
|
99
|
+
if (brushA > data[idx]) {
|
|
100
|
+
data[idx] = brushA
|
|
101
|
+
tileChanged = true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (tileChanged) changed = true
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
return changed
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
paintBinaryMask(
|
|
112
|
+
brush: PaintBinaryMask,
|
|
113
|
+
alpha: number,
|
|
114
|
+
x: number,
|
|
115
|
+
y: number,
|
|
116
|
+
): boolean
|
|
117
|
+
paintBinaryMask(
|
|
118
|
+
brush: PaintBinaryMask,
|
|
119
|
+
alpha: number,
|
|
120
|
+
startX: number,
|
|
121
|
+
startY: number,
|
|
122
|
+
endX: number,
|
|
123
|
+
endY: number,
|
|
124
|
+
): boolean
|
|
125
|
+
paintBinaryMask(
|
|
126
|
+
brush: PaintBinaryMask,
|
|
127
|
+
alpha: number,
|
|
128
|
+
x0: number,
|
|
129
|
+
y0: number,
|
|
130
|
+
x1: number = x0,
|
|
131
|
+
y1: number = y0,
|
|
132
|
+
): boolean {
|
|
133
|
+
if (alpha === 0) return false
|
|
134
|
+
|
|
135
|
+
const scratch = this.scratchBounds
|
|
136
|
+
const lookup = this.lookup
|
|
137
|
+
const tilePool = this.tilePool
|
|
138
|
+
const config = this.config
|
|
139
|
+
const tileShift = config.tileShift
|
|
140
|
+
const tileMask = config.tileMask
|
|
141
|
+
const target = config.target
|
|
142
|
+
|
|
143
|
+
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
144
|
+
let changed = false
|
|
145
|
+
|
|
146
|
+
const trimRectBoundsFn = this.trimRectBoundsFn
|
|
147
|
+
const eachTileInBoundsFn = this.eachTileInBoundsFn
|
|
148
|
+
|
|
149
|
+
this.forEachLinePointFn(x0, y0, x1, y1, (px, py) => {
|
|
150
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
151
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
152
|
+
|
|
153
|
+
trimRectBoundsFn(
|
|
154
|
+
topLeftX,
|
|
155
|
+
topLeftY,
|
|
156
|
+
bW,
|
|
157
|
+
bH,
|
|
158
|
+
target.w,
|
|
159
|
+
target.h,
|
|
160
|
+
scratch,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
164
|
+
|
|
165
|
+
eachTileInBoundsFn(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
166
|
+
const data = tile.data
|
|
167
|
+
let tileChanged = false
|
|
168
|
+
|
|
169
|
+
for (let i = 0; i < bH_t; i++) {
|
|
170
|
+
const canvasY = bY + i
|
|
171
|
+
const bOff = (canvasY - topLeftY) * bW
|
|
172
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
173
|
+
const dS = tOff + (bX & tileMask)
|
|
174
|
+
|
|
175
|
+
for (let j = 0; j < bW_t; j++) {
|
|
176
|
+
const canvasX = bX + j
|
|
177
|
+
if (bD[bOff + (canvasX - topLeftX)]) {
|
|
178
|
+
const idx = dS + j
|
|
179
|
+
if (data[idx] < alpha) {
|
|
180
|
+
data[idx] = alpha
|
|
181
|
+
tileChanged = true
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (tileChanged) changed = true
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
return changed
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
paintRect(
|
|
194
|
+
alpha: number,
|
|
195
|
+
brushWidth: number,
|
|
196
|
+
brushHeight: number,
|
|
197
|
+
x: number,
|
|
198
|
+
y: number,
|
|
199
|
+
): boolean
|
|
200
|
+
paintRect(
|
|
201
|
+
alpha: number,
|
|
202
|
+
brushWidth: number,
|
|
203
|
+
brushHeight: number,
|
|
204
|
+
startX: number,
|
|
205
|
+
startY: number,
|
|
206
|
+
endX: number,
|
|
207
|
+
endY: number,
|
|
208
|
+
): boolean
|
|
209
|
+
paintRect(
|
|
210
|
+
alpha: number,
|
|
211
|
+
brushWidth: number,
|
|
212
|
+
brushHeight: number,
|
|
213
|
+
x0: number,
|
|
214
|
+
y0: number,
|
|
215
|
+
x1: number = x0,
|
|
216
|
+
y1: number = y0,
|
|
217
|
+
): boolean {
|
|
218
|
+
const scratch = this.scratchBounds
|
|
219
|
+
const lookup = this.lookup
|
|
220
|
+
const tilePool = this.tilePool
|
|
221
|
+
const config = this.config
|
|
222
|
+
const tileShift = config.tileShift
|
|
223
|
+
const tileMask = config.tileMask
|
|
224
|
+
const target = config.target
|
|
225
|
+
|
|
226
|
+
const centerOffsetX = _macro_paintRectCenterOffset(brushWidth)
|
|
227
|
+
const centerOffsetY = _macro_paintRectCenterOffset(brushHeight)
|
|
228
|
+
|
|
229
|
+
const trimRectBoundsFn = this.trimRectBoundsFn
|
|
230
|
+
const eachTileInBoundsFn = this.eachTileInBoundsFn
|
|
231
|
+
|
|
232
|
+
let changed = false
|
|
233
|
+
this.forEachLinePointFn(
|
|
234
|
+
x0,
|
|
235
|
+
y0,
|
|
236
|
+
x1,
|
|
237
|
+
y1,
|
|
238
|
+
(px, py) => {
|
|
239
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
240
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
241
|
+
|
|
242
|
+
trimRectBoundsFn(
|
|
243
|
+
topLeftX,
|
|
244
|
+
topLeftY,
|
|
245
|
+
brushWidth,
|
|
246
|
+
brushHeight,
|
|
247
|
+
target.w,
|
|
248
|
+
target.h,
|
|
249
|
+
scratch,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
253
|
+
|
|
254
|
+
eachTileInBoundsFn(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
255
|
+
const data = tile.data
|
|
256
|
+
let tileChanged = false
|
|
257
|
+
|
|
258
|
+
for (let i = 0; i < bH_t; i++) {
|
|
259
|
+
const canvasY = bY + i
|
|
260
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
261
|
+
const dS = tOff + (bX & tileMask)
|
|
262
|
+
|
|
263
|
+
for (let j = 0; j < bW_t; j++) {
|
|
264
|
+
const idx = dS + j
|
|
265
|
+
|
|
266
|
+
// If the new alpha is stronger than the current alpha, overwrite it
|
|
267
|
+
if (alpha > data[idx]) {
|
|
268
|
+
data[idx] = alpha
|
|
269
|
+
tileChanged = true
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (tileChanged) {
|
|
275
|
+
changed = true
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
},
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
return changed
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private opts = {
|
|
286
|
+
alpha: 255,
|
|
287
|
+
blendFn: sourceOverPerfect,
|
|
288
|
+
x: 0,
|
|
289
|
+
y: 0,
|
|
290
|
+
w: 0,
|
|
291
|
+
h: 0,
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
commit(
|
|
295
|
+
accumulator: PixelAccumulator,
|
|
296
|
+
color: Color32,
|
|
297
|
+
alpha = 255,
|
|
298
|
+
blendFn = sourceOverPerfect,
|
|
299
|
+
) {
|
|
300
|
+
const blendColorPixelDataAlphaMaskFn = this.blendColorPixelDataAlphaMaskFn
|
|
301
|
+
const tileShift = this.config.tileShift
|
|
302
|
+
const lookup = this.lookup
|
|
303
|
+
const opts = this.opts
|
|
304
|
+
|
|
305
|
+
opts.alpha = alpha
|
|
306
|
+
opts.blendFn = blendFn
|
|
307
|
+
|
|
308
|
+
for (let i = 0; i < lookup.length; i++) {
|
|
309
|
+
const tile = lookup[i]
|
|
310
|
+
|
|
311
|
+
if (tile) {
|
|
312
|
+
const didChange = accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty)
|
|
313
|
+
|
|
314
|
+
const dx = tile.tx << tileShift
|
|
315
|
+
const dy = tile.ty << tileShift
|
|
316
|
+
|
|
317
|
+
opts.x = dx
|
|
318
|
+
opts.y = dy
|
|
319
|
+
opts.w = tile.w
|
|
320
|
+
opts.h = tile.h
|
|
321
|
+
|
|
322
|
+
didChange(
|
|
323
|
+
blendColorPixelDataAlphaMaskFn(
|
|
324
|
+
this.config.target,
|
|
325
|
+
color,
|
|
326
|
+
tile,
|
|
327
|
+
opts,
|
|
328
|
+
),
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
this.clear()
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
clear(): void {
|
|
337
|
+
this.tilePool.releaseTiles(this.lookup)
|
|
338
|
+
}
|
|
339
|
+
}
|