pixel-data-js 0.23.1 → 0.24.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 (66) hide show
  1. package/dist/index.dev.cjs +1024 -596
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1010 -592
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1024 -596
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +280 -165
  8. package/dist/index.prod.js +1010 -592
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +3 -2
  11. package/src/Canvas/CanvasFrameRenderer.ts +57 -0
  12. package/src/Canvas/ReusableCanvas.ts +60 -11
  13. package/src/History/HistoryAction.ts +38 -0
  14. package/src/History/HistoryManager.ts +4 -8
  15. package/src/History/PixelAccumulator.ts +95 -80
  16. package/src/History/PixelEngineConfig.ts +18 -6
  17. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +6 -6
  18. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +6 -6
  19. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +6 -5
  20. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +22 -22
  21. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +6 -5
  22. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +19 -19
  23. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +6 -4
  24. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +20 -20
  25. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +6 -4
  26. package/src/History/PixelMutator/mutatorBlendColor.ts +8 -5
  27. package/src/History/PixelMutator/mutatorBlendColorCircleMask.ts +71 -0
  28. package/src/History/PixelMutator/mutatorBlendPixel.ts +22 -26
  29. package/src/History/PixelMutator/mutatorBlendPixelData.ts +5 -3
  30. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +5 -3
  31. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +5 -3
  32. package/src/History/PixelMutator/mutatorClear.ts +6 -5
  33. package/src/History/PixelMutator/mutatorFill.ts +34 -9
  34. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +4 -2
  35. package/src/History/PixelMutator/mutatorInvert.ts +8 -4
  36. package/src/History/PixelMutator.ts +4 -3
  37. package/src/History/PixelPatchTiles.ts +3 -15
  38. package/src/History/PixelWriter.ts +29 -33
  39. package/src/ImageData/ReusableImageData.ts +3 -5
  40. package/src/Mask/{CircleBrushAlphaMask.ts → CircleAlphaMask.ts} +2 -2
  41. package/src/Mask/{CircleBrushBinaryMask.ts → CircleBinaryMask.ts} +2 -2
  42. package/src/PixelData/PixelData.ts +1 -27
  43. package/src/PixelData/applyAlphaMaskToPixelData.ts +19 -9
  44. package/src/PixelData/applyBinaryMaskToPixelData.ts +24 -17
  45. package/src/PixelData/applyRectBrushToPixelData.ts +18 -5
  46. package/src/PixelData/blendColorPixelData.ts +31 -7
  47. package/src/PixelData/blendColorPixelDataAlphaMask.ts +16 -6
  48. package/src/PixelData/blendColorPixelDataBinaryMask.ts +16 -7
  49. package/src/PixelData/{applyCircleBrushToPixelData.ts → blendColorPixelDataCircleMask.ts} +11 -10
  50. package/src/PixelData/blendPixel.ts +47 -0
  51. package/src/PixelData/blendPixelData.ts +14 -4
  52. package/src/PixelData/blendPixelDataAlphaMask.ts +12 -4
  53. package/src/PixelData/blendPixelDataBinaryMask.ts +13 -4
  54. package/src/PixelData/blendPixelDataPaintBuffer.ts +37 -0
  55. package/src/PixelData/clearPixelData.ts +2 -2
  56. package/src/PixelData/fillPixelData.ts +26 -16
  57. package/src/PixelData/fillPixelDataBinaryMask.ts +12 -4
  58. package/src/PixelData/fillPixelDataFast.ts +94 -0
  59. package/src/PixelData/invertPixelData.ts +4 -2
  60. package/src/PixelTile/PaintBuffer.ts +122 -0
  61. package/src/PixelTile/PaintBufferRenderer.ts +40 -0
  62. package/src/PixelTile/PixelTile.ts +21 -0
  63. package/src/PixelTile/PixelTilePool.ts +63 -0
  64. package/src/_types.ts +9 -9
  65. package/src/index.ts +16 -6
  66. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +0 -78
@@ -0,0 +1,122 @@
1
+ import type { AlphaMaskRect, BinaryMaskRect, Color32, Rect } from '../_types'
2
+ import type { PixelEngineConfig } from '../History/PixelEngineConfig'
3
+ import type { PixelTile } from './PixelTile'
4
+ import type { PixelTilePool } from './PixelTilePool'
5
+
6
+ export class PaintBuffer {
7
+ readonly lookup: (PixelTile | undefined)[]
8
+
9
+ constructor(
10
+ readonly config: PixelEngineConfig,
11
+ readonly tilePool: PixelTilePool,
12
+ ) {
13
+ this.lookup = []
14
+ }
15
+
16
+ private processMaskTiles(
17
+ mask: Rect,
18
+ callback: (tile: PixelTile, bX: number, bY: number, bW: number, bH: number, mX: number, mY: number) => void,
19
+ ): void {
20
+ const { tileShift, targetColumns } = this.config
21
+
22
+ const x1 = mask.x >> tileShift
23
+ const y1 = mask.y >> tileShift
24
+ const x2 = (mask.x + mask.w - 1) >> tileShift
25
+ const y2 = (mask.y + mask.h - 1) >> tileShift
26
+
27
+ for (let ty = y1; ty <= y2; ty++) {
28
+ const tileRowIndex = ty * targetColumns
29
+ const tileTop = ty << tileShift
30
+
31
+ for (let tx = x1; tx <= x2; tx++) {
32
+ const id = tileRowIndex + tx
33
+ let tile = this.lookup[id]
34
+
35
+ if (!tile) {
36
+ tile = this.tilePool.getTile(id, tx, ty)
37
+ this.lookup[id] = tile
38
+ }
39
+
40
+ const tileLeft = tx << tileShift
41
+
42
+ const startX = Math.max(mask.x, tileLeft)
43
+ const endX = Math.min(mask.x + mask.w, tileLeft + this.config.tileSize)
44
+ const startY = Math.max(mask.y, tileTop)
45
+ const endY = Math.min(mask.y + mask.h, tileTop + this.config.tileSize)
46
+
47
+ // Passing 7 primitive arguments to avoid object allocation
48
+ callback(
49
+ tile,
50
+ startX,
51
+ startY,
52
+ endX - startX,
53
+ endY - startY,
54
+ startX - mask.x,
55
+ startY - mask.y,
56
+ )
57
+ }
58
+ }
59
+ }
60
+
61
+ writeColorBinaryMaskRect(color: Color32, mask: BinaryMaskRect): void {
62
+ const { tileShift, tileMask } = this.config
63
+ const maskData = mask.data
64
+ const maskW = mask.w
65
+
66
+ this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
67
+ const data32 = tile.data32
68
+ const startTileX = bX & tileMask
69
+
70
+ for (let i = 0; i < bH; i++) {
71
+ const tileY = (bY + i) & tileMask
72
+ const maskY = mY + i
73
+ const tileRowOffset = tileY << tileShift
74
+ const maskRowOffset = maskY * maskW
75
+
76
+ const destStart = tileRowOffset + startTileX
77
+ const maskStart = maskRowOffset + mX
78
+
79
+ for (let j = 0; j < bW; j++) {
80
+ if (maskData[maskStart + j]) {
81
+ data32[destStart + j] = color
82
+ }
83
+ }
84
+ }
85
+ })
86
+ }
87
+
88
+ writeColorAlphaMaskRect(color: Color32, mask: AlphaMaskRect): void {
89
+ const { tileShift, tileMask } = this.config
90
+ const maskData = mask.data
91
+ const maskW = mask.w
92
+ const colorRGB = color & 0x00ffffff
93
+ const colorA = color >>> 24
94
+
95
+ this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
96
+ const data32 = tile.data32
97
+ const startTileX = bX & tileMask
98
+
99
+ for (let i = 0; i < bH; i++) {
100
+ const tileY = (bY + i) & tileMask
101
+ const maskY = mY + i
102
+ const tileRowOffset = tileY << tileShift
103
+ const maskRowOffset = maskY * maskW
104
+
105
+ const destStart = tileRowOffset + startTileX
106
+ const maskStart = maskRowOffset + mX
107
+
108
+ for (let j = 0; j < bW; j++) {
109
+ const maskA = maskData[maskStart + j]
110
+ if (maskA > 0) {
111
+ const finalA = (colorA * maskA + 128) >> 8
112
+ data32[destStart + j] = (colorRGB | (finalA << 24)) >>> 0 as Color32
113
+ }
114
+ }
115
+ }
116
+ })
117
+ }
118
+
119
+ clear(): void {
120
+ this.tilePool.releaseTiles(this.lookup)
121
+ }
122
+ }
@@ -0,0 +1,40 @@
1
+ import { CANVAS_CTX_FAILED } from '../Canvas/_constants'
2
+ import type { PaintBuffer } from './PaintBuffer'
3
+
4
+ export type PaintBufferRenderer = ReturnType<typeof makePaintBufferRenderer>
5
+
6
+ /**
7
+ *
8
+ * @param paintBuffer
9
+ * @param offscreenCanvasClass - @internal
10
+ */
11
+ export function makePaintBufferRenderer(
12
+ paintBuffer: PaintBuffer,
13
+ offscreenCanvasClass = OffscreenCanvas,
14
+ ) {
15
+ const config = paintBuffer.config
16
+ const tileSize = config.tileSize
17
+ const tileShift = config.tileShift
18
+ const lookup = paintBuffer.lookup
19
+ const canvas = new offscreenCanvasClass(tileSize, tileSize)
20
+ const ctx = canvas.getContext('2d')
21
+ if (!ctx) throw new Error(CANVAS_CTX_FAILED)
22
+ ctx.imageSmoothingEnabled = false
23
+
24
+ return function drawPaintBuffer(
25
+ target: CanvasRenderingContext2D,
26
+ ): void {
27
+ for (let i = 0; i < lookup.length; i++) {
28
+ const tile = lookup[i]
29
+
30
+ if (tile) {
31
+ const dx = tile.tx << tileShift
32
+ const dy = tile.ty << tileShift
33
+
34
+ ctx.putImageData(tile.imageData, 0, 0)
35
+
36
+ target.drawImage(canvas, dx, dy)
37
+ }
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,21 @@
1
+ import type { IPixelData } from '../_types'
2
+
3
+ export class PixelTile implements IPixelData {
4
+ readonly data32: Uint32Array
5
+ readonly width: number
6
+ readonly height: number
7
+ readonly imageData: ImageData
8
+
9
+ constructor(
10
+ public id: number,
11
+ public tx: number,
12
+ public ty: number,
13
+ tileSize: number,
14
+ tileArea: number,
15
+ ) {
16
+ this.width = this.height = tileSize
17
+ this.data32 = new Uint32Array(tileArea)
18
+ const data8 = new Uint8ClampedArray(this.data32.buffer) as Uint8ClampedArray<ArrayBuffer>
19
+ this.imageData = new ImageData(data8, tileSize, tileSize)
20
+ }
21
+ }
@@ -0,0 +1,63 @@
1
+ import type { PixelEngineConfig } from '../History/PixelEngineConfig'
2
+
3
+ import { PixelTile } from './PixelTile'
4
+
5
+ export class PixelTilePool {
6
+ public pool: PixelTile[]
7
+
8
+ private tileSize: number
9
+ private tileArea: number
10
+
11
+ constructor(
12
+ config: PixelEngineConfig,
13
+ ) {
14
+ this.pool = []
15
+ this.tileSize = config.tileSize
16
+ this.tileArea = config.tileArea
17
+ }
18
+
19
+ getTile(
20
+ id: number,
21
+ tx: number,
22
+ ty: number,
23
+ ): PixelTile {
24
+ let tile = this.pool.pop()
25
+
26
+ if (tile) {
27
+ tile.id = id
28
+ tile.tx = tx
29
+ tile.ty = ty
30
+
31
+ // Wipe dirty memory from previous uses before handing it out
32
+ tile.data32.fill(0)
33
+
34
+ return tile
35
+ }
36
+
37
+ return new PixelTile(
38
+ id,
39
+ tx,
40
+ ty,
41
+ this.tileSize,
42
+ this.tileArea,
43
+ )
44
+ }
45
+
46
+ releaseTile(tile: PixelTile): void {
47
+ this.pool.push(tile)
48
+ }
49
+
50
+ releaseTiles(tiles: (PixelTile | undefined)[]): void {
51
+ let length = tiles.length
52
+
53
+ for (let i = 0; i < length; i++) {
54
+ let tile = tiles[i]
55
+
56
+ if (tile) {
57
+ this.pool.push(tile)
58
+ }
59
+ }
60
+
61
+ tiles.length = 0
62
+ }
63
+ }
package/src/_types.ts CHANGED
@@ -80,19 +80,19 @@ export interface AlphaMask extends Mask {
80
80
  readonly type: MaskType.ALPHA
81
81
  }
82
82
 
83
- interface CircleBrush {
83
+ interface BaseCircleMask {
84
84
  readonly size: number
85
85
  readonly radius: number
86
86
  readonly minOffset: number
87
87
  }
88
88
 
89
- export interface CircleBrushAlphaMask extends CircleBrush, AlphaMask {
89
+ export interface CircleAlphaMask extends BaseCircleMask, AlphaMask {
90
90
  }
91
91
 
92
- export interface CircleBrushBinaryMask extends CircleBrush, BinaryMask {
92
+ export interface CircleBinaryMask extends BaseCircleMask, BinaryMask {
93
93
  }
94
94
 
95
- export type CircleBrushMask = CircleBrushAlphaMask | CircleBrushBinaryMask
95
+ export type CircleMask = CircleAlphaMask | CircleBinaryMask
96
96
 
97
97
  /**
98
98
  * Configuration for pixel manipulation operations.
@@ -217,6 +217,11 @@ export type BinaryMaskRect = Rect & {
217
217
  data: Uint8Array
218
218
  }
219
219
 
220
+ export type AlphaMaskRect = Rect & {
221
+ type: MaskType.ALPHA
222
+ data: Uint8Array
223
+ }
224
+
220
225
  export type NullableBinaryMaskRect = Rect & ({
221
226
  type: MaskType.BINARY
222
227
  data: Uint8Array
@@ -225,7 +230,6 @@ export type NullableBinaryMaskRect = Rect & ({
225
230
  data?: null
226
231
  })
227
232
 
228
-
229
233
  export type NullableMaskRect = Rect & ({
230
234
  type: MaskType
231
235
  data: Uint8Array
@@ -234,10 +238,6 @@ export type NullableMaskRect = Rect & ({
234
238
  data?: null
235
239
  })
236
240
 
237
- export type AlphaMaskRect = Rect & {
238
- mask: AlphaMask
239
- }
240
-
241
241
  export type HistoryMutator<T extends {}, D extends {}> = (writer: PixelWriter<any>, deps?: Partial<D>) => T
242
242
 
243
243
  export interface IPixelData {
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export * from './_types'
3
3
  export * from './color'
4
4
 
5
5
  export * from './Algorithm/floodFillSelection'
6
+ export * from './Algorithm/forEachLinePoint'
6
7
 
7
8
  export * from './BlendModes/blend-modes-fast'
8
9
  export * from './BlendModes/blend-modes-perfect'
@@ -11,6 +12,7 @@ export * from './BlendModes/BlendModeRegistry'
11
12
  export * from './BlendModes/toBlendModeIndexAndName'
12
13
 
13
14
  export * from './Canvas/_constants'
15
+ export * from './Canvas/CanvasFrameRenderer'
14
16
  export * from './Canvas/PixelCanvas'
15
17
  export * from './Canvas/ReusableCanvas'
16
18
 
@@ -18,6 +20,7 @@ export * from './Clipboard/getImageDataFromClipboard'
18
20
  export * from './Clipboard/writeImageDataToClipboard'
19
21
  export * from './Clipboard/writeImgBlobToClipboard'
20
22
 
23
+ export * from './History/HistoryAction'
21
24
  export * from './History/HistoryManager'
22
25
  export * from './History/PixelAccumulator'
23
26
  export * from './History/PixelEngineConfig'
@@ -28,8 +31,8 @@ export * from './History/PixelWriter'
28
31
  export * from './History/PixelMutator/mutatorApplyAlphaMask'
29
32
  export * from './History/PixelMutator/mutatorApplyBinaryMask'
30
33
 
31
- export * from './History/PixelMutator/mutatorApplyCircleBrush'
32
34
  export * from './History/PixelMutator/mutatorApplyCircleBrushStroke'
35
+ export * from './History/PixelMutator/mutatorBlendColorCircleMask'
33
36
 
34
37
  export * from './History/PixelMutator/mutatorApplyCirclePencil'
35
38
  export * from './History/PixelMutator/mutatorApplyCirclePencilStroke'
@@ -80,8 +83,8 @@ export * from './Input/getSupportedRasterFormats'
80
83
  export * from './Mask/AlphaMask'
81
84
  export * from './Mask/BinaryMask'
82
85
 
83
- export * from './Mask/CircleBrushAlphaMask'
84
- export * from './Mask/CircleBrushBinaryMask'
86
+ export * from './Mask/CircleAlphaMask'
87
+ export * from './Mask/CircleBinaryMask'
85
88
 
86
89
  export * from './Mask/applyBinaryMaskToAlphaMask'
87
90
  export * from './Mask/copyMask'
@@ -92,13 +95,13 @@ export * from './Mask/mergeAlphaMasks'
92
95
  export * from './Mask/mergeBinaryMasks'
93
96
  export * from './Mask/setMaskData'
94
97
 
95
- export { subtractBinaryMaskRects } from './MaskRect/subtractBinaryMaskRects'
96
98
  export * from './MaskRect/merge2BinaryMaskRects'
97
99
  export * from './MaskRect/mergeBinaryMaskRects'
100
+ export * from './MaskRect/subtractBinaryMaskRects'
98
101
 
99
102
  export * from './PixelData/applyAlphaMaskToPixelData'
100
103
  export * from './PixelData/applyBinaryMaskToPixelData'
101
- export * from './PixelData/applyCircleBrushToPixelData'
104
+ export * from './PixelData/blendColorPixelDataCircleMask'
102
105
  export * from './PixelData/PixelData'
103
106
 
104
107
  export * from './PixelData/applyRectBrushToPixelData'
@@ -109,11 +112,14 @@ export * from './PixelData/blendPixelData'
109
112
  export * from './PixelData/blendPixelDataAlphaMask'
110
113
  export * from './PixelData/blendPixelDataBinaryMask'
111
114
 
115
+ export * from './PixelData/blendPixel'
116
+ export * from './PixelData/blendPixelDataPaintBuffer'
112
117
  export * from './PixelData/clearPixelData'
113
118
  export * from './PixelData/extractPixelData'
114
119
  export * from './PixelData/extractPixelDataBuffer'
115
120
  export * from './PixelData/fillPixelData'
116
121
  export * from './PixelData/fillPixelDataBinaryMask'
122
+ export * from './PixelData/fillPixelDataFast'
117
123
  export * from './PixelData/invertPixelData'
118
124
  export * from './PixelData/PixelBuffer32'
119
125
  export * from './PixelData/pixelDataToAlphaMask'
@@ -129,4 +135,8 @@ export * from './Rect/getRectBrushOrPencilStrokeBounds'
129
135
  export * from './Rect/getRectsBounds'
130
136
  export * from './Rect/trimRectBounds'
131
137
 
132
- export * from './Algorithm/forEachLinePoint'
138
+ export * from './PixelTile/PaintBuffer'
139
+ export * from './PixelTile/PaintBufferRenderer'
140
+ export * from './PixelTile/PixelTile'
141
+ export * from './PixelTile/PixelTilePool'
142
+
@@ -1,78 +0,0 @@
1
- import type {
2
- BlendColor32,
3
- CircleBrushAlphaMask,
4
- Color32,
5
- ColorBlendMaskOptions,
6
- HistoryMutator,
7
- Rect,
8
- } from '../../_types'
9
- import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
10
- import { applyCircleBrushToPixelData } from '../../PixelData/applyCircleBrushToPixelData'
11
- import { getCircleBrushOrPencilBounds } from '../../Rect/getCircleBrushOrPencilBounds'
12
- import { PixelWriter } from '../PixelWriter'
13
-
14
- const defaults = {
15
- applyCircleBrushToPixelData,
16
- getCircleBrushOrPencilBounds,
17
- }
18
-
19
- type Deps = Partial<typeof defaults>
20
-
21
- /**
22
- * @param deps - @hidden
23
- */
24
- export const mutatorApplyCircleBrush = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
25
- const {
26
- applyCircleBrushToPixelData = defaults.applyCircleBrushToPixelData,
27
- getCircleBrushOrPencilBounds = defaults.getCircleBrushOrPencilBounds,
28
-
29
- } = deps
30
-
31
- const boundsOut: Rect = { x: 0, y: 0, w: 0, h: 0 }
32
-
33
- const blendColorPixelOptions: ColorBlendMaskOptions = {
34
- alpha: 255,
35
- blendFn: sourceOverPerfect,
36
- x: 0,
37
- y: 0,
38
- w: 0,
39
- h: 0,
40
- }
41
-
42
- return {
43
- applyCircleBrush(
44
- color: Color32,
45
- centerX: number,
46
- centerY: number,
47
- brush: CircleBrushAlphaMask,
48
- alpha = 255,
49
- blendFn?: BlendColor32,
50
- ) {
51
-
52
- const bounds = getCircleBrushOrPencilBounds(
53
- centerX,
54
- centerY,
55
- brush.size,
56
- writer.target.width,
57
- writer.target.height,
58
- boundsOut,
59
- )
60
-
61
- const { x, y, w, h } = bounds
62
-
63
- writer.accumulator.storeRegionBeforeState(x, y, w, h)
64
-
65
- applyCircleBrushToPixelData(
66
- writer.target,
67
- color,
68
- centerX,
69
- centerY,
70
- brush,
71
- alpha,
72
- blendFn,
73
- blendColorPixelOptions,
74
- bounds,
75
- )
76
- },
77
- }
78
- }) satisfies HistoryMutator<any, Deps>