pixel-data-js 0.27.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.
Files changed (139) hide show
  1. package/README.md +12 -2
  2. package/dist/index.prod.cjs +2222 -1045
  3. package/dist/index.prod.cjs.map +1 -1
  4. package/dist/index.prod.d.ts +542 -417
  5. package/dist/index.prod.js +2167 -1024
  6. package/dist/index.prod.js.map +1 -1
  7. package/package.json +11 -11
  8. package/src/Algorithm/floodFillSelection.ts +8 -6
  9. package/src/Algorithm/forEachLinePoint.ts +6 -6
  10. package/src/{Internal/resample32.ts → Algorithm/resampleUint32Array.ts} +11 -21
  11. package/src/BlendModes/blend-modes-fast.ts +169 -0
  12. package/src/BlendModes/blend-modes-perfect.ts +207 -0
  13. package/src/BlendModes/blend-modes.ts +9 -0
  14. package/src/Canvas/CanvasFrameRenderer.ts +20 -28
  15. package/src/Canvas/CanvasPixelDataRenderer.ts +23 -0
  16. package/src/Canvas/PixelCanvas.ts +2 -7
  17. package/src/Canvas/ReusableCanvas.ts +4 -12
  18. package/src/Canvas/_canvas-types.ts +26 -0
  19. package/src/History/PixelAccumulator.ts +17 -17
  20. package/src/History/PixelEngineConfig.ts +3 -3
  21. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +4 -3
  22. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +4 -3
  23. package/src/History/PixelMutator/mutatorApplyMask.ts +4 -3
  24. package/src/History/PixelMutator/mutatorBlendAlphaMask.ts +6 -4
  25. package/src/History/PixelMutator/mutatorBlendBinaryMask.ts +6 -4
  26. package/src/History/PixelMutator/mutatorBlendColor.ts +2 -2
  27. package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +2 -1
  28. package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +2 -1
  29. package/src/History/PixelMutator/mutatorBlendColorPaintMask.ts +3 -1
  30. package/src/History/PixelMutator/mutatorBlendColorPaintRect.ts +3 -3
  31. package/src/History/PixelMutator/mutatorBlendMask.ts +6 -4
  32. package/src/History/PixelMutator/mutatorBlendPixelData.ts +5 -4
  33. package/src/History/PixelMutator/mutatorClear.ts +4 -3
  34. package/src/History/PixelMutator/mutatorFill.ts +5 -4
  35. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +2 -1
  36. package/src/History/PixelMutator/mutatorInvert.ts +2 -2
  37. package/src/History/PixelMutator.ts +1 -1
  38. package/src/History/PixelPatchTiles.ts +7 -7
  39. package/src/History/PixelWriter.ts +12 -63
  40. package/src/ImageData/ImageDataLike.ts +1 -1
  41. package/src/ImageData/_ImageData-types.ts +13 -0
  42. package/src/ImageData/copyImageData.ts +1 -1
  43. package/src/ImageData/extractImageDataBuffer.ts +3 -2
  44. package/src/ImageData/imageDataToUint32Array.ts +18 -0
  45. package/src/ImageData/resampleImageData.ts +3 -3
  46. package/src/ImageData/resizeImageData.ts +1 -1
  47. package/src/ImageData/serialization.ts +1 -1
  48. package/src/ImageData/uInt32ArrayToImageData.ts +1 -1
  49. package/src/ImageData/writeImageData.ts +2 -2
  50. package/src/ImageData/writeImageDataBuffer.ts +2 -2
  51. package/src/IndexedImage/IndexedImage.ts +56 -98
  52. package/src/IndexedImage/_indexedImage-types.ts +18 -0
  53. package/src/IndexedImage/getIndexedImageColorCounts.ts +3 -3
  54. package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
  55. package/src/IndexedImage/indexedImageToImageData.ts +4 -6
  56. package/src/IndexedImage/resampleIndexedImage.ts +7 -15
  57. package/src/Input/fileToImageData.ts +1 -1
  58. package/src/Internal/_errors.ts +2 -0
  59. package/src/Internal/macros.ts +14 -0
  60. package/src/Mask/AlphaMask.ts +1 -1
  61. package/src/Mask/BinaryMask/makeBinaryMaskFromAlphaMask.ts +23 -0
  62. package/src/Mask/BinaryMask/makeBinaryMaskOutline.ts +88 -0
  63. package/src/Mask/BinaryMask/makeCircleBinaryMaskOutline.ts +104 -0
  64. package/src/Mask/BinaryMask/makeRectBinaryMaskOutline.ts +34 -0
  65. package/src/Mask/BinaryMask.ts +1 -1
  66. package/src/Mask/_mask-types.ts +73 -0
  67. package/src/Mask/applyBinaryMaskToAlphaMask.ts +2 -1
  68. package/src/Mask/copyMask.ts +1 -1
  69. package/src/Mask/extractMask.ts +2 -1
  70. package/src/Mask/extractMaskBuffer.ts +1 -1
  71. package/src/Mask/mergeAlphaMasks.ts +6 -3
  72. package/src/Mask/mergeBinaryMasks.ts +2 -1
  73. package/src/Mask/setMaskData.ts +1 -1
  74. package/src/MaskRect/merge2BinaryMaskRects.ts +2 -2
  75. package/src/MaskRect/mergeBinaryMaskRects.ts +1 -1
  76. package/src/MaskRect/subtractBinaryMaskRects.ts +1 -1
  77. package/src/Paint/AlphaMaskPaintBuffer.ts +339 -0
  78. package/src/Paint/AlphaMaskPaintBufferCanvasRenderer.ts +78 -0
  79. package/src/Paint/BinaryMaskPaintBuffer.ts +254 -0
  80. package/src/Paint/BinaryMaskPaintBufferCanvasRenderer.ts +67 -0
  81. package/src/Paint/{PaintBuffer.ts → ColorPaintBuffer.ts} +148 -77
  82. package/src/Paint/{PaintBufferCanvasRenderer.ts → ColorPaintBufferCanvasRenderer.ts} +6 -5
  83. package/src/Paint/PaintCursorRenderer.ts +117 -0
  84. package/src/Paint/_paint-types.ts +22 -0
  85. package/src/Paint/eachTileInBounds.ts +45 -0
  86. package/src/Paint/makeCirclePaintMask.ts +74 -0
  87. package/src/Paint/makePaintMask.ts +5 -2
  88. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +4 -2
  89. package/src/PixelData/PixelData.ts +15 -19
  90. package/src/PixelData/ReusablePixelData.ts +36 -0
  91. package/src/PixelData/_pixelData-types.ts +17 -0
  92. package/src/PixelData/applyAlphaMaskToPixelData.ts +80 -43
  93. package/src/PixelData/applyBinaryMaskToPixelData.ts +10 -8
  94. package/src/PixelData/applyMaskToPixelData.ts +4 -9
  95. package/src/PixelData/blendColorPixelData.ts +9 -8
  96. package/src/PixelData/blendColorPixelDataAlphaMask.ts +9 -7
  97. package/src/PixelData/blendColorPixelDataBinaryMask.ts +9 -7
  98. package/src/PixelData/blendColorPixelDataMask.ts +4 -2
  99. package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +4 -2
  100. package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +4 -2
  101. package/src/PixelData/blendColorPixelDataPaintMask.ts +5 -2
  102. package/src/PixelData/blendPixel.ts +6 -5
  103. package/src/PixelData/blendPixelData.ts +14 -13
  104. package/src/PixelData/blendPixelDataAlphaMask.ts +15 -13
  105. package/src/PixelData/blendPixelDataBinaryMask.ts +15 -13
  106. package/src/PixelData/blendPixelDataMask.ts +5 -3
  107. package/src/PixelData/blendPixelDataPaintBuffer.ts +5 -4
  108. package/src/PixelData/clearPixelDataFast.ts +4 -2
  109. package/src/PixelData/copyPixelData.ts +14 -0
  110. package/src/PixelData/extractPixelData.ts +8 -7
  111. package/src/PixelData/extractPixelDataBuffer.ts +9 -8
  112. package/src/PixelData/fillPixelData.ts +16 -14
  113. package/src/PixelData/fillPixelDataBinaryMask.ts +10 -8
  114. package/src/PixelData/fillPixelDataFast.ts +16 -14
  115. package/src/PixelData/invertPixelData.ts +9 -8
  116. package/src/PixelData/pixelDataToAlphaMask.ts +9 -8
  117. package/src/PixelData/reflectPixelData.ts +9 -9
  118. package/src/PixelData/resamplePixelData.ts +20 -9
  119. package/src/PixelData/rotatePixelData.ts +8 -7
  120. package/src/PixelData/uInt32ArrayToPixelData.ts +15 -0
  121. package/src/PixelData/writePaintBufferToPixelData.ts +5 -5
  122. package/src/PixelData/writePixelDataBuffer.ts +10 -9
  123. package/src/Rect/_rect-types.ts +7 -0
  124. package/src/Rect/getRectsBounds.ts +1 -1
  125. package/src/Rect/trimMaskRectBounds.ts +2 -1
  126. package/src/Rect/trimRectBounds.ts +1 -1
  127. package/src/Tile/MaskTile.ts +40 -0
  128. package/src/Tile/PixelTile.ts +23 -0
  129. package/src/{PixelTile/PixelTilePool.ts → Tile/TilePool.ts} +9 -9
  130. package/src/Tile/_tile-types.ts +33 -0
  131. package/src/_errors.ts +1 -0
  132. package/src/_types.ts +2 -118
  133. package/src/index.ts +46 -21
  134. package/src/ImageData/imageDataToUInt32Array.ts +0 -13
  135. package/src/Internal/helpers.ts +0 -5
  136. package/src/Paint/makeCirclePaintAlphaMask.ts +0 -41
  137. package/src/Paint/makeCirclePaintBinaryMask.ts +0 -29
  138. package/src/PixelTile/PixelTile.ts +0 -21
  139. /package/src/{Internal → Rect}/resolveClipping.ts +0 -0
@@ -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
+ }
@@ -1,78 +1,64 @@
1
- import type { Color32, PaintAlphaMask, PaintBinaryMask, Rect } from '../_types'
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/helpers'
5
- import type { PixelTile } from '../PixelTile/PixelTile'
6
- import type { PixelTilePool } from '../PixelTile/PixelTilePool'
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 PaintBuffer {
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: PixelTilePool,
21
+ readonly tilePool: TilePool<PixelTile>,
22
+ private blendPixelDataFn = blendPixelData,
16
23
  ) {
17
24
  this.lookup = []
18
25
  }
19
26
 
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(
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 { tileShift, tileMask, target } = this.config
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.width,
89
- target.height,
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
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
96
- const d32 = tile.data32
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
- writePaintBinaryMaskStroke(
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.width,
156
- target.height,
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
- this.eachTileInBounds(scratch, (tile, bX, bY, bW_t, bH_t) => {
163
- const d32 = tile.data32
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
- writeRectStroke(
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 = -_macro_paintRectCenterOffset(brushWidth)
209
- const centerOffsetY = -_macro_paintRectCenterOffset(brushHeight)
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.width,
228
- target.height,
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
- this.eachTileInBounds(
235
- scratch,
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 '../../support/error-strings'
2
- import type { PaintBuffer } from './PaintBuffer'
1
+ import { CANVAS_CTX_FAILED } from '../Internal/_errors'
2
+ import type { ColorPaintBuffer } from './ColorPaintBuffer'
3
3
 
4
- export type PaintBufferCanvasRenderer = ReturnType<typeof makePaintBufferCanvasRenderer>
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 makePaintBufferCanvasRenderer(
11
- paintBuffer: PaintBuffer,
11
+ export function makeColorPaintBufferCanvasRenderer(
12
+ paintBuffer: ColorPaintBuffer,
12
13
  offscreenCanvasClass = OffscreenCanvas,
13
14
  ) {
14
15
  const config = paintBuffer.config