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,78 @@
|
|
|
1
|
+
import type { Color32 } from '../_types'
|
|
2
|
+
import { CANVAS_CTX_FAILED } from '../Internal/_errors'
|
|
3
|
+
import { makePixelData } from '../PixelData/PixelData'
|
|
4
|
+
import type { AlphaMaskPaintBuffer } from './AlphaMaskPaintBuffer'
|
|
5
|
+
|
|
6
|
+
export type AlphaMaskPaintBufferCanvasRenderer = ReturnType<typeof makeAlphaMaskPaintBufferCanvasRenderer>
|
|
7
|
+
|
|
8
|
+
export function makeAlphaMaskPaintBufferCanvasRenderer(
|
|
9
|
+
paintBuffer: AlphaMaskPaintBuffer,
|
|
10
|
+
offscreenCanvasClass = OffscreenCanvas,
|
|
11
|
+
) {
|
|
12
|
+
const config = paintBuffer.config
|
|
13
|
+
const tileSize = config.tileSize
|
|
14
|
+
const tileShift = config.tileShift
|
|
15
|
+
const tileArea = config.tileArea
|
|
16
|
+
const lookup = paintBuffer.lookup
|
|
17
|
+
|
|
18
|
+
const canvas = new offscreenCanvasClass(tileSize, tileSize)
|
|
19
|
+
const ctx = canvas.getContext('2d')
|
|
20
|
+
|
|
21
|
+
if (!ctx) throw new Error(CANVAS_CTX_FAILED)
|
|
22
|
+
|
|
23
|
+
ctx.imageSmoothingEnabled = false
|
|
24
|
+
|
|
25
|
+
const bridge = makePixelData(new ImageData(tileSize, tileSize))
|
|
26
|
+
const view32 = bridge.data
|
|
27
|
+
|
|
28
|
+
return function drawPaintBuffer(
|
|
29
|
+
targetCtx: CanvasRenderingContext2D,
|
|
30
|
+
color: Color32,
|
|
31
|
+
alpha = 255,
|
|
32
|
+
compOperation: GlobalCompositeOperation = 'source-over',
|
|
33
|
+
): void {
|
|
34
|
+
if (alpha === 0) return
|
|
35
|
+
|
|
36
|
+
const baseSrcAlpha = (color >>> 24)
|
|
37
|
+
const colorRGB = color & 0x00ffffff
|
|
38
|
+
|
|
39
|
+
if (baseSrcAlpha === 0) return
|
|
40
|
+
|
|
41
|
+
targetCtx.globalAlpha = alpha / 255
|
|
42
|
+
targetCtx.globalCompositeOperation = compOperation
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < lookup.length; i++) {
|
|
45
|
+
const tile = lookup[i]
|
|
46
|
+
|
|
47
|
+
if (tile) {
|
|
48
|
+
const data8 = tile.data
|
|
49
|
+
view32.fill(0)
|
|
50
|
+
|
|
51
|
+
for (let p = 0; p < tileArea; p++) {
|
|
52
|
+
const maskA = data8[p]
|
|
53
|
+
if (maskA === 0) continue
|
|
54
|
+
|
|
55
|
+
// If mask is solid, the final pixel is just the unmodified color
|
|
56
|
+
if (maskA === 255) {
|
|
57
|
+
view32[p] = color
|
|
58
|
+
} else {
|
|
59
|
+
// Otherwise, blend the color's inherent alpha with the mask's alpha
|
|
60
|
+
const t = baseSrcAlpha * maskA + 128
|
|
61
|
+
const finalA = (t + (t >> 8)) >> 8
|
|
62
|
+
|
|
63
|
+
view32[p] = ((colorRGB | (finalA << 24)) >>> 0) as Color32
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const dx = tile.tx << tileShift
|
|
68
|
+
const dy = tile.ty << tileShift
|
|
69
|
+
|
|
70
|
+
ctx.putImageData(bridge.imageData, 0, 0)
|
|
71
|
+
targetCtx.drawImage(canvas, dx, dy)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
targetCtx.globalAlpha = 1
|
|
76
|
+
targetCtx.globalCompositeOperation = 'source-over'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
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 { blendColorPixelDataBinaryMask } from '../PixelData/blendColorPixelDataBinaryMask'
|
|
8
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
9
|
+
import { trimRectBounds } from '../Rect/trimRectBounds'
|
|
10
|
+
import type { BinaryMaskTile } from '../Tile/_tile-types'
|
|
11
|
+
import type { TilePool } from '../Tile/TilePool'
|
|
12
|
+
import type { PaintBinaryMask } from './_paint-types'
|
|
13
|
+
import { eachTileInBounds } from './eachTileInBounds'
|
|
14
|
+
|
|
15
|
+
export class BinaryMaskPaintBuffer {
|
|
16
|
+
readonly lookup: (BinaryMaskTile | undefined)[]
|
|
17
|
+
private readonly scratchBounds: Rect = { x: 0, y: 0, w: 0, h: 0 }
|
|
18
|
+
|
|
19
|
+
private blendColorPixelDataBinaryMaskFn = blendColorPixelDataBinaryMask
|
|
20
|
+
private forEachLinePointFn = forEachLinePoint
|
|
21
|
+
private trimRectBoundsFn = trimRectBounds
|
|
22
|
+
private eachTileInBoundsFn = eachTileInBounds
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
readonly config: PixelEngineConfig,
|
|
26
|
+
readonly tilePool: TilePool<BinaryMaskTile>,
|
|
27
|
+
) {
|
|
28
|
+
this.lookup = []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
paintBinaryMask(
|
|
32
|
+
brush: PaintBinaryMask,
|
|
33
|
+
x: number,
|
|
34
|
+
y: number,
|
|
35
|
+
): boolean
|
|
36
|
+
paintBinaryMask(
|
|
37
|
+
brush: PaintBinaryMask,
|
|
38
|
+
startX: number,
|
|
39
|
+
startY: number,
|
|
40
|
+
endX: number,
|
|
41
|
+
endY: number,
|
|
42
|
+
): boolean
|
|
43
|
+
paintBinaryMask(
|
|
44
|
+
brush: PaintBinaryMask,
|
|
45
|
+
x0: number,
|
|
46
|
+
y0: number,
|
|
47
|
+
x1: number = x0,
|
|
48
|
+
y1: number = y0,
|
|
49
|
+
): boolean {
|
|
50
|
+
const scratch = this.scratchBounds
|
|
51
|
+
const lookup = this.lookup
|
|
52
|
+
const tilePool = this.tilePool
|
|
53
|
+
const config = this.config
|
|
54
|
+
const tileShift = config.tileShift
|
|
55
|
+
const tileMask = config.tileMask
|
|
56
|
+
const target = config.target
|
|
57
|
+
|
|
58
|
+
const { w: bW, h: bH, data: bD, centerOffsetX, centerOffsetY } = brush
|
|
59
|
+
let changed = false
|
|
60
|
+
|
|
61
|
+
const trimRectBoundsFn = this.trimRectBoundsFn
|
|
62
|
+
const eachTileInBoundsFn = this.eachTileInBoundsFn
|
|
63
|
+
|
|
64
|
+
this.forEachLinePointFn(x0, y0, x1, y1, (px, py) => {
|
|
65
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
66
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
67
|
+
|
|
68
|
+
trimRectBoundsFn(
|
|
69
|
+
topLeftX,
|
|
70
|
+
topLeftY,
|
|
71
|
+
bW,
|
|
72
|
+
bH,
|
|
73
|
+
target.w,
|
|
74
|
+
target.h,
|
|
75
|
+
scratch,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
79
|
+
|
|
80
|
+
eachTileInBoundsFn(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
81
|
+
const data = tile.data
|
|
82
|
+
let tileChanged = false
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < bH_t; i++) {
|
|
85
|
+
const canvasY = bY + i
|
|
86
|
+
const bOff = (canvasY - topLeftY) * bW
|
|
87
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
88
|
+
const dS = tOff + (bX & tileMask)
|
|
89
|
+
|
|
90
|
+
for (let j = 0; j < bW_t; j++) {
|
|
91
|
+
const canvasX = bX + j
|
|
92
|
+
|
|
93
|
+
if (bD[bOff + (canvasX - topLeftX)]) {
|
|
94
|
+
const idx = dS + j
|
|
95
|
+
|
|
96
|
+
// Only write if it's not already "on"
|
|
97
|
+
if (data[idx] === 0) {
|
|
98
|
+
data[idx] = 1
|
|
99
|
+
tileChanged = true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (tileChanged) changed = true
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
return changed
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
paintRect(
|
|
112
|
+
brushWidth: number,
|
|
113
|
+
brushHeight: number,
|
|
114
|
+
x: number,
|
|
115
|
+
y: number,
|
|
116
|
+
): boolean
|
|
117
|
+
paintRect(
|
|
118
|
+
brushWidth: number,
|
|
119
|
+
brushHeight: number,
|
|
120
|
+
startX: number,
|
|
121
|
+
startY: number,
|
|
122
|
+
endX: number,
|
|
123
|
+
endY: number,
|
|
124
|
+
): boolean
|
|
125
|
+
paintRect(
|
|
126
|
+
brushWidth: number,
|
|
127
|
+
brushHeight: number,
|
|
128
|
+
x0: number,
|
|
129
|
+
y0: number,
|
|
130
|
+
x1: number = x0,
|
|
131
|
+
y1: number = y0,
|
|
132
|
+
): boolean {
|
|
133
|
+
const scratch = this.scratchBounds
|
|
134
|
+
const lookup = this.lookup
|
|
135
|
+
const tilePool = this.tilePool
|
|
136
|
+
const config = this.config
|
|
137
|
+
const tileShift = config.tileShift
|
|
138
|
+
const tileMask = config.tileMask
|
|
139
|
+
const target = config.target
|
|
140
|
+
|
|
141
|
+
const centerOffsetX = _macro_paintRectCenterOffset(brushWidth)
|
|
142
|
+
const centerOffsetY = _macro_paintRectCenterOffset(brushHeight)
|
|
143
|
+
|
|
144
|
+
const trimRectBoundsFn = this.trimRectBoundsFn
|
|
145
|
+
const eachTileInBoundsFn = this.eachTileInBoundsFn
|
|
146
|
+
|
|
147
|
+
let changed = false
|
|
148
|
+
this.forEachLinePointFn(
|
|
149
|
+
x0,
|
|
150
|
+
y0,
|
|
151
|
+
x1,
|
|
152
|
+
y1,
|
|
153
|
+
(px, py) => {
|
|
154
|
+
const topLeftX = Math.floor(px + centerOffsetX)
|
|
155
|
+
const topLeftY = Math.floor(py + centerOffsetY)
|
|
156
|
+
|
|
157
|
+
trimRectBoundsFn(
|
|
158
|
+
topLeftX,
|
|
159
|
+
topLeftY,
|
|
160
|
+
brushWidth,
|
|
161
|
+
brushHeight,
|
|
162
|
+
target.w,
|
|
163
|
+
target.h,
|
|
164
|
+
scratch,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if (scratch.w <= 0 || scratch.h <= 0) return
|
|
168
|
+
|
|
169
|
+
eachTileInBoundsFn(config, lookup, tilePool, scratch, (tile, bX, bY, bW_t, bH_t) => {
|
|
170
|
+
const data = tile.data
|
|
171
|
+
let tileChanged = false
|
|
172
|
+
|
|
173
|
+
for (let i = 0; i < bH_t; i++) {
|
|
174
|
+
const canvasY = bY + i
|
|
175
|
+
const tOff = (canvasY & tileMask) << tileShift
|
|
176
|
+
const dS = tOff + (bX & tileMask)
|
|
177
|
+
|
|
178
|
+
for (let j = 0; j < bW_t; j++) {
|
|
179
|
+
const idx = dS + j
|
|
180
|
+
|
|
181
|
+
// Only write if it's not already "on"
|
|
182
|
+
if (data[idx] === 0) {
|
|
183
|
+
data[idx] = 1
|
|
184
|
+
tileChanged = true
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (tileChanged) {
|
|
190
|
+
changed = true
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
)
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return changed
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private opts = {
|
|
201
|
+
alpha: 255,
|
|
202
|
+
blendFn: sourceOverPerfect,
|
|
203
|
+
x: 0,
|
|
204
|
+
y: 0,
|
|
205
|
+
w: 0,
|
|
206
|
+
h: 0,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
commit(
|
|
210
|
+
accumulator: PixelAccumulator,
|
|
211
|
+
color: Color32,
|
|
212
|
+
alpha = 255,
|
|
213
|
+
blendFn = sourceOverPerfect,
|
|
214
|
+
) {
|
|
215
|
+
const blendColorPixelDataBinaryMaskFn = this.blendColorPixelDataBinaryMaskFn
|
|
216
|
+
const tileShift = this.config.tileShift
|
|
217
|
+
const lookup = this.lookup
|
|
218
|
+
const opts = this.opts
|
|
219
|
+
|
|
220
|
+
opts.alpha = alpha
|
|
221
|
+
opts.blendFn = blendFn
|
|
222
|
+
|
|
223
|
+
for (let i = 0; i < lookup.length; i++) {
|
|
224
|
+
const tile = lookup[i]
|
|
225
|
+
|
|
226
|
+
if (tile) {
|
|
227
|
+
const didChange = accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty)
|
|
228
|
+
|
|
229
|
+
const dx = tile.tx << tileShift
|
|
230
|
+
const dy = tile.ty << tileShift
|
|
231
|
+
|
|
232
|
+
opts.x = dx
|
|
233
|
+
opts.y = dy
|
|
234
|
+
opts.w = tile.w
|
|
235
|
+
opts.h = tile.h
|
|
236
|
+
|
|
237
|
+
didChange(
|
|
238
|
+
blendColorPixelDataBinaryMaskFn(
|
|
239
|
+
this.config.target,
|
|
240
|
+
color,
|
|
241
|
+
tile,
|
|
242
|
+
opts,
|
|
243
|
+
),
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
this.clear()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
clear(): void {
|
|
252
|
+
this.tilePool.releaseTiles(this.lookup)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Color32 } from '../_types'
|
|
2
|
+
import { CANVAS_CTX_FAILED } from '../Internal/_errors'
|
|
3
|
+
import { makePixelData } from '../PixelData/PixelData'
|
|
4
|
+
import type { BinaryMaskPaintBuffer } from './BinaryMaskPaintBuffer'
|
|
5
|
+
|
|
6
|
+
export type BinaryMaskPaintBufferCanvasRenderer = ReturnType<typeof makeBinaryMaskPaintBufferCanvasRenderer>
|
|
7
|
+
|
|
8
|
+
export function makeBinaryMaskPaintBufferCanvasRenderer(
|
|
9
|
+
paintBuffer: BinaryMaskPaintBuffer,
|
|
10
|
+
offscreenCanvasClass = OffscreenCanvas,
|
|
11
|
+
) {
|
|
12
|
+
const config = paintBuffer.config
|
|
13
|
+
const tileSize = config.tileSize
|
|
14
|
+
const tileShift = config.tileShift
|
|
15
|
+
const tileArea = config.tileArea
|
|
16
|
+
const lookup = paintBuffer.lookup
|
|
17
|
+
|
|
18
|
+
const canvas = new offscreenCanvasClass(tileSize, tileSize)
|
|
19
|
+
const ctx = canvas.getContext('2d')
|
|
20
|
+
|
|
21
|
+
if (!ctx) throw new Error(CANVAS_CTX_FAILED)
|
|
22
|
+
|
|
23
|
+
ctx.imageSmoothingEnabled = false
|
|
24
|
+
|
|
25
|
+
const bridge = makePixelData(new ImageData(tileSize, tileSize))
|
|
26
|
+
const view32 = bridge.data
|
|
27
|
+
|
|
28
|
+
return function drawPaintBuffer(
|
|
29
|
+
targetCtx: CanvasRenderingContext2D,
|
|
30
|
+
color: Color32,
|
|
31
|
+
alpha = 255,
|
|
32
|
+
compOperation: GlobalCompositeOperation = 'source-over',
|
|
33
|
+
): void {
|
|
34
|
+
if (alpha === 0) return
|
|
35
|
+
|
|
36
|
+
const baseSrcAlpha = (color >>> 24)
|
|
37
|
+
if (baseSrcAlpha === 0) return
|
|
38
|
+
|
|
39
|
+
targetCtx.globalAlpha = alpha / 255
|
|
40
|
+
targetCtx.globalCompositeOperation = compOperation
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < lookup.length; i++) {
|
|
43
|
+
const tile = lookup[i]
|
|
44
|
+
|
|
45
|
+
if (tile) {
|
|
46
|
+
const data8 = tile.data
|
|
47
|
+
view32.fill(0)
|
|
48
|
+
|
|
49
|
+
for (let p = 0; p < tileArea; p++) {
|
|
50
|
+
// If mask is solid, the final pixel is just the unmodified color
|
|
51
|
+
if (data8[p] === 1) {
|
|
52
|
+
view32[p] = color
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const dx = tile.tx << tileShift
|
|
57
|
+
const dy = tile.ty << tileShift
|
|
58
|
+
|
|
59
|
+
ctx.putImageData(bridge.imageData, 0, 0)
|
|
60
|
+
targetCtx.drawImage(canvas, dx, dy)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
targetCtx.globalAlpha = 1
|
|
65
|
+
targetCtx.globalCompositeOperation = 'source-over'
|
|
66
|
+
}
|
|
67
|
+
}
|