pixel-data-js 0.35.0 → 0.37.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 (84) hide show
  1. package/dist/index.prod.cjs +521 -326
  2. package/dist/index.prod.cjs.map +1 -1
  3. package/dist/index.prod.d.ts +132 -78
  4. package/dist/index.prod.js +513 -324
  5. package/dist/index.prod.js.map +1 -1
  6. package/package.json +2 -2
  7. package/src/Algorithm/floodFillSelection.ts +3 -2
  8. package/src/BlendModes/blend-modes-fast.ts +2 -1
  9. package/src/BlendModes/blend-modes-perfect.ts +2 -1
  10. package/src/Canvas/ReusableCanvas.ts +0 -5
  11. package/src/Color/_color-types.ts +8 -0
  12. package/src/Color/colorDistance.ts +9 -0
  13. package/src/Color/convert-color.ts +43 -0
  14. package/src/Color/lerpColor32.ts +44 -0
  15. package/src/Color/pack-color.ts +38 -0
  16. package/src/History/HistoryAction.ts +2 -2
  17. package/src/History/PixelAccumulator.ts +32 -13
  18. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +2 -0
  19. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +5 -1
  20. package/src/History/PixelMutator/mutatorApplyMask.ts +1 -0
  21. package/src/History/PixelMutator/mutatorBlendAlphaMask.ts +1 -0
  22. package/src/History/PixelMutator/mutatorBlendBinaryMask.ts +1 -0
  23. package/src/History/PixelMutator/mutatorBlendColor.ts +4 -1
  24. package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +2 -1
  25. package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +2 -1
  26. package/src/History/PixelMutator/mutatorBlendColorPaintMask.ts +24 -8
  27. package/src/History/PixelMutator/mutatorBlendColorPaintRect.ts +4 -1
  28. package/src/History/PixelMutator/mutatorBlendMask.ts +1 -0
  29. package/src/History/PixelMutator/mutatorBlendPixel.ts +3 -1
  30. package/src/History/PixelMutator/mutatorBlendPixelData.ts +1 -0
  31. package/src/History/PixelMutator/mutatorClear.ts +3 -2
  32. package/src/History/PixelMutator/mutatorFill.ts +54 -38
  33. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +3 -2
  34. package/src/History/PixelMutator/mutatorInvert.ts +3 -2
  35. package/src/History/PixelMutator.ts +1 -2
  36. package/src/History/PixelWriter.ts +5 -5
  37. package/src/IndexedImage/IndexedImage.ts +1 -1
  38. package/src/IndexedImage/indexedImageToAverageColor.ts +3 -2
  39. package/src/Mask/_mask-types.ts +9 -0
  40. package/src/Paint/AlphaMaskPaintBuffer.ts +26 -26
  41. package/src/Paint/BinaryMaskPaintBuffer.ts +19 -19
  42. package/src/Paint/ColorPaintBuffer.ts +40 -42
  43. package/src/Paint/Commit/AlphaMaskPaintBufferCommitter.ts +1 -1
  44. package/src/Paint/Commit/AlphaMaskPaintBufferManager.ts +6 -7
  45. package/src/Paint/Commit/BinaryMaskPaintBufferCommitter.ts +1 -1
  46. package/src/Paint/Commit/BinaryMaskPaintBufferManager.ts +6 -7
  47. package/src/Paint/Commit/ColorPaintBufferManager.ts +6 -7
  48. package/src/Paint/Commit/commitColorPaintBuffer.ts +2 -6
  49. package/src/Paint/Commit/commitMaskPaintBuffer.ts +3 -7
  50. package/src/Paint/Render/AlphaMaskPaintBufferCanvasRenderer.ts +42 -25
  51. package/src/Paint/Render/BinaryMaskPaintBufferCanvasRenderer.ts +40 -24
  52. package/src/Paint/Render/ColorPaintBufferCanvasRenderer.ts +21 -21
  53. package/src/Paint/Render/PaintCursorRenderer.ts +12 -2
  54. package/src/Paint/eachTileInBounds.ts +9 -10
  55. package/src/PixelData/_pixelData-types.ts +7 -0
  56. package/src/PixelData/blendColorPixelData.ts +2 -1
  57. package/src/PixelData/blendColorPixelDataAlphaMask.ts +2 -1
  58. package/src/PixelData/blendColorPixelDataBinaryMask.ts +2 -1
  59. package/src/PixelData/blendColorPixelDataMask.ts +2 -1
  60. package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +1 -1
  61. package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +1 -1
  62. package/src/PixelData/blendColorPixelDataPaintMask.ts +19 -8
  63. package/src/PixelData/blendPixel.ts +2 -1
  64. package/src/PixelData/blendPixelData.ts +2 -1
  65. package/src/PixelData/blendPixelDataAlphaMask.ts +2 -1
  66. package/src/PixelData/blendPixelDataBinaryMask.ts +2 -1
  67. package/src/PixelData/blendPixelDataPaintBuffer.ts +2 -3
  68. package/src/PixelData/clearPixelDataFast.ts +1 -1
  69. package/src/PixelData/cropPixelData.ts +36 -0
  70. package/src/PixelData/fillPixelData.ts +7 -7
  71. package/src/PixelData/fillPixelDataBinaryMask.ts +1 -1
  72. package/src/PixelData/fillPixelDataFast.ts +1 -1
  73. package/src/PixelData/trimPixelData.ts +49 -0
  74. package/src/PixelData/writePaintBufferToPixelData.ts +1 -5
  75. package/src/Tile/MaskTile.ts +4 -0
  76. package/src/Tile/PixelTile.ts +2 -0
  77. package/src/Tile/TilePool.ts +9 -8
  78. package/src/Tile/TileTargetConfig.ts +27 -0
  79. package/src/Tile/_tile-types.ts +16 -0
  80. package/src/_types.ts +1 -6
  81. package/src/index.ts +9 -3
  82. package/src/History/PixelEngineConfig.ts +0 -28
  83. package/src/Internal/_constants.ts +0 -3
  84. package/src/color.ts +0 -112
@@ -1,5 +1,5 @@
1
- import { type Color32 } from '../../_types'
2
1
  import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
2
+ import type { Color32 } from '../../Color/_color-types'
3
3
  import type { PixelAccumulator } from '../../History/PixelAccumulator'
4
4
  import { blendColorPixelDataAlphaMask } from '../../PixelData/blendColorPixelDataAlphaMask'
5
5
  import { blendColorPixelDataBinaryMask } from '../../PixelData/blendColorPixelDataBinaryMask'
@@ -42,7 +42,6 @@ export function commitMaskPaintBuffer(
42
42
  blendColorPixelDataMaskFn: any,
43
43
  ) {
44
44
  const config = accumulator.config
45
- const tileShift = config.tileShift
46
45
  const lookup = paintBuffer.lookup
47
46
 
48
47
  SCRATCH_OPTS.alpha = alpha
@@ -54,11 +53,8 @@ export function commitMaskPaintBuffer(
54
53
  if (tile) {
55
54
  const didChange = accumulator.storeTileBeforeState(tile.id, tile.tx, tile.ty)
56
55
 
57
- const dx = tile.tx << tileShift
58
- const dy = tile.ty << tileShift
59
-
60
- SCRATCH_OPTS.x = dx
61
- SCRATCH_OPTS.y = dy
56
+ SCRATCH_OPTS.x = tile.x
57
+ SCRATCH_OPTS.y = tile.y
62
58
  SCRATCH_OPTS.w = tile.w
63
59
  SCRATCH_OPTS.h = tile.h
64
60
 
@@ -1,31 +1,46 @@
1
- import type { Color32 } from '../../_types'
2
- import type { CanvasObjectFactory } from '../../Canvas/_canvas-types'
3
- import { DEFAULT_CANVAS_FACTORY } from '../../Internal/_constants'
4
- import { CANVAS_CTX_FAILED } from '../../Internal/_errors'
5
- import { makePixelData } from '../../PixelData/PixelData'
1
+ import type { ReusableCanvasFactory } from '../../Canvas/_canvas-types'
2
+ import { makeReusableOffscreenCanvas } from '../../Canvas/ReusableCanvas'
3
+ import type { Color32 } from '../../Color/_color-types'
4
+ import type { PixelData } from '../../PixelData/_pixelData-types'
5
+ import { makeReusablePixelData } from '../../PixelData/ReusablePixelData'
6
+ import type { AlphaMaskTile, TileTargetMeta } from '../../Tile/_tile-types'
6
7
  import type { AlphaMaskPaintBuffer } from '../AlphaMaskPaintBuffer'
7
8
 
8
9
  export type AlphaMaskPaintBufferCanvasRenderer = ReturnType<typeof makeAlphaMaskPaintBufferCanvasRenderer>
9
10
 
10
- export function makeAlphaMaskPaintBufferCanvasRenderer(
11
+ export function makeAlphaMaskPaintBufferCanvasRenderer<T extends HTMLCanvasElement | OffscreenCanvas>(
11
12
  paintBuffer: AlphaMaskPaintBuffer,
12
- canvasFactory: CanvasObjectFactory<any> = DEFAULT_CANVAS_FACTORY,
13
+ reusableCanvasFactory?: () => ReusableCanvasFactory<T>,
13
14
  ) {
14
- const config = paintBuffer.config
15
- const tileSize = config.tileSize
16
- const tileShift = config.tileShift
17
- const tileArea = config.tileArea
18
- const lookup = paintBuffer.lookup
19
-
20
- const canvas = canvasFactory(tileSize, tileSize)
21
- const ctx = canvas.getContext('2d')
22
- if (!ctx) throw new Error(CANVAS_CTX_FAILED)
23
- ctx.imageSmoothingEnabled = false
24
-
25
- const bridge = makePixelData(new ImageData(tileSize, tileSize))
26
- const view32 = bridge.data
15
+ const factory = (reusableCanvasFactory ?? makeReusableOffscreenCanvas) as unknown as () => ReusableCanvasFactory<T>
16
+ const getBuffer = factory()
17
+ const getBridge = makeReusablePixelData()
18
+
19
+ let config: TileTargetMeta
20
+ let tileSize: number
21
+ let tileArea: number
22
+ let lookup: (AlphaMaskTile | undefined)[]
23
+ let view32: Uint32Array
24
+ let bridge: PixelData
25
+ let canvas: HTMLCanvasElement | OffscreenCanvas
26
+ let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
27
+
28
+ setBuffer(paintBuffer)
29
+
30
+ function setBuffer(value: AlphaMaskPaintBuffer) {
31
+ paintBuffer = value
32
+ config = paintBuffer.config
33
+ tileSize = config.tileSize
34
+ tileArea = config.tileArea
35
+ lookup = paintBuffer.lookup
36
+ bridge = getBridge(tileSize, tileSize)
37
+ view32 = bridge.data
38
+ const buff = getBuffer(tileSize, tileSize)
39
+ canvas = buff.canvas
40
+ ctx = buff.ctx
41
+ }
27
42
 
28
- return function drawPaintBuffer(
43
+ function draw(
29
44
  targetCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
30
45
  color: Color32,
31
46
  alpha = 255,
@@ -64,15 +79,17 @@ export function makeAlphaMaskPaintBufferCanvasRenderer(
64
79
  }
65
80
  }
66
81
 
67
- const dx = tile.tx << tileShift
68
- const dy = tile.ty << tileShift
69
-
70
82
  ctx.putImageData(bridge.imageData, 0, 0)
71
- targetCtx.drawImage(canvas, dx, dy)
83
+ targetCtx.drawImage(canvas, tile.x, tile.y)
72
84
  }
73
85
  }
74
86
 
75
87
  targetCtx.globalAlpha = 1
76
88
  targetCtx.globalCompositeOperation = 'source-over'
77
89
  }
90
+
91
+ return {
92
+ draw,
93
+ setBuffer,
94
+ }
78
95
  }
@@ -1,31 +1,46 @@
1
- import type { Color32 } from '../../_types'
2
- import type { CanvasObjectFactory } from '../../Canvas/_canvas-types'
3
- import { DEFAULT_CANVAS_FACTORY } from '../../Internal/_constants'
4
- import { CANVAS_CTX_FAILED } from '../../Internal/_errors'
5
- import { makePixelData } from '../../PixelData/PixelData'
1
+ import type { ReusableCanvasFactory } from '../../Canvas/_canvas-types'
2
+ import { makeReusableOffscreenCanvas } from '../../Canvas/ReusableCanvas'
3
+ import type { Color32 } from '../../Color/_color-types'
4
+ import type { PixelData } from '../../PixelData/_pixelData-types'
5
+ import { makeReusablePixelData } from '../../PixelData/ReusablePixelData'
6
+ import type { BinaryMaskTile, TileTargetMeta } from '../../Tile/_tile-types'
6
7
  import type { BinaryMaskPaintBuffer } from '../BinaryMaskPaintBuffer'
7
8
 
8
9
  export type BinaryMaskPaintBufferCanvasRenderer = ReturnType<typeof makeBinaryMaskPaintBufferCanvasRenderer>
9
10
 
10
- export function makeBinaryMaskPaintBufferCanvasRenderer(
11
+ export function makeBinaryMaskPaintBufferCanvasRenderer<T extends HTMLCanvasElement | OffscreenCanvas>(
11
12
  paintBuffer: BinaryMaskPaintBuffer,
12
- canvasFactory: CanvasObjectFactory<any> = DEFAULT_CANVAS_FACTORY,
13
+ reusableCanvasFactory?: () => ReusableCanvasFactory<T>,
13
14
  ) {
14
- const config = paintBuffer.config
15
- const tileSize = config.tileSize
16
- const tileShift = config.tileShift
17
- const tileArea = config.tileArea
18
- const lookup = paintBuffer.lookup
15
+ const factory = (reusableCanvasFactory ?? makeReusableOffscreenCanvas) as unknown as () => ReusableCanvasFactory<T>
16
+ const getBuffer = factory()
17
+ const getBridge = makeReusablePixelData()
19
18
 
20
- const canvas = canvasFactory(tileSize, tileSize)
21
- const ctx = canvas.getContext('2d')
22
- if (!ctx) throw new Error(CANVAS_CTX_FAILED)
23
- ctx.imageSmoothingEnabled = false
19
+ let config: TileTargetMeta
20
+ let tileSize: number
21
+ let tileArea: number
22
+ let lookup: (BinaryMaskTile | undefined)[]
23
+ let view32: Uint32Array
24
+ let bridge: PixelData
25
+ let canvas: HTMLCanvasElement | OffscreenCanvas
26
+ let ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D
24
27
 
25
- const bridge = makePixelData(new ImageData(tileSize, tileSize))
26
- const view32 = bridge.data
28
+ setBuffer(paintBuffer)
27
29
 
28
- return function drawPaintBuffer(
30
+ function setBuffer(value: BinaryMaskPaintBuffer) {
31
+ paintBuffer = value
32
+ config = paintBuffer.config
33
+ tileSize = config.tileSize
34
+ tileArea = config.tileArea
35
+ lookup = paintBuffer.lookup
36
+ bridge = getBridge(tileSize, tileSize)
37
+ view32 = bridge.data
38
+ const buff = getBuffer(tileSize, tileSize)
39
+ canvas = buff.canvas
40
+ ctx = buff.ctx
41
+ }
42
+
43
+ function draw(
29
44
  targetCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
30
45
  color: Color32,
31
46
  alpha = 255,
@@ -47,21 +62,22 @@ export function makeBinaryMaskPaintBufferCanvasRenderer(
47
62
  view32.fill(0)
48
63
 
49
64
  for (let p = 0; p < tileArea; p++) {
50
- // If mask is solid, the final pixel is just the unmodified color
51
65
  if (data8[p] === 1) {
52
66
  view32[p] = color
53
67
  }
54
68
  }
55
69
 
56
- const dx = tile.tx << tileShift
57
- const dy = tile.ty << tileShift
58
-
59
70
  ctx.putImageData(bridge.imageData, 0, 0)
60
- targetCtx.drawImage(canvas, dx, dy)
71
+ targetCtx.drawImage(canvas, tile.x, tile.y)
61
72
  }
62
73
  }
63
74
 
64
75
  targetCtx.globalAlpha = 1
65
76
  targetCtx.globalCompositeOperation = 'source-over'
66
77
  }
78
+
79
+ return {
80
+ draw,
81
+ setBuffer,
82
+ }
67
83
  }
@@ -1,47 +1,47 @@
1
- import type { CanvasObjectFactory } from '../../Canvas/_canvas-types'
2
- import { DEFAULT_CANVAS_FACTORY } from '../../Internal/_constants'
3
- import { CANVAS_CTX_FAILED } from '../../Internal/_errors'
1
+ import type { ReusableCanvasFactory } from '../../Canvas/_canvas-types'
2
+ import { makeReusableOffscreenCanvas } from '../../Canvas/ReusableCanvas'
4
3
  import type { ColorPaintBuffer } from '../ColorPaintBuffer'
5
4
 
6
5
  export type ColorPaintBufferCanvasRenderer = ReturnType<typeof makeColorPaintBufferCanvasRenderer>
7
6
 
8
- export function makeColorPaintBufferCanvasRenderer(
7
+ export function makeColorPaintBufferCanvasRenderer<T extends HTMLCanvasElement | OffscreenCanvas>(
9
8
  paintBuffer: ColorPaintBuffer,
10
- canvasFactory: CanvasObjectFactory<any> = DEFAULT_CANVAS_FACTORY,
9
+ reusableCanvasFactory?: () => ReusableCanvasFactory<T>,
11
10
  ) {
12
- const config = paintBuffer.config
13
- const tileSize = config.tileSize
14
- const tileShift = config.tileShift
15
- const lookup = paintBuffer.lookup
11
+ const factory = (reusableCanvasFactory ?? makeReusableOffscreenCanvas) as unknown as () => ReusableCanvasFactory<T>
12
+ const getBuffer = factory()
16
13
 
17
- const canvas = canvasFactory(tileSize, tileSize)
18
- const ctx = canvas.getContext('2d')
19
- if (!ctx) throw new Error(CANVAS_CTX_FAILED)
20
- ctx.imageSmoothingEnabled = false
21
-
22
- return function drawPaintBuffer(
14
+ function draw(
23
15
  targetCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
24
16
  alpha = 255,
25
17
  compOperation: GlobalCompositeOperation = 'source-over',
26
18
  ): void {
19
+ const buff = getBuffer(paintBuffer.config.tileSize, paintBuffer.config.tileSize)
20
+ const lookup = paintBuffer.lookup
21
+ const length = lookup.length
22
+ const ctx = buff.ctx
23
+ const canvas = buff.canvas
27
24
 
28
25
  targetCtx.globalAlpha = alpha / 255
29
26
  targetCtx.globalCompositeOperation = compOperation
30
27
 
31
- for (let i = 0; i < lookup.length; i++) {
28
+ for (let i = 0; i < length; i++) {
32
29
  const tile = lookup[i]
33
30
 
34
31
  if (tile) {
35
- const dx = tile.tx << tileShift
36
- const dy = tile.ty << tileShift
37
-
38
32
  ctx.putImageData(tile.imageData, 0, 0)
39
-
40
- targetCtx.drawImage(canvas, dx, dy)
33
+ targetCtx.drawImage(canvas, tile.x, tile.y)
41
34
  }
42
35
  }
43
36
 
44
37
  targetCtx.globalAlpha = 1
45
38
  targetCtx.globalCompositeOperation = 'source-over'
46
39
  }
40
+
41
+ return {
42
+ draw,
43
+ setBuffer(value: ColorPaintBuffer) {
44
+ paintBuffer = value
45
+ },
46
+ }
47
47
  }
@@ -1,7 +1,8 @@
1
- import { type Color32 } from '../../_types'
2
1
  import type { ReusableCanvasFactory } from '../../Canvas/_canvas-types'
3
2
  import { makeReusableOffscreenCanvas } from '../../Canvas/ReusableCanvas'
4
- import { packColor } from '../../color'
3
+ import type { Color32 } from '../../Color/_color-types'
4
+
5
+ import { packColor } from '../../Color/pack-color'
5
6
  import { _macro_paintRectCenterOffset } from '../../Internal/macros'
6
7
  import { type BinaryMask, MaskType } from '../../Mask/_mask-types'
7
8
  import { makeBinaryMaskFromAlphaMask } from '../../Mask/BinaryMask/makeBinaryMaskFromAlphaMask'
@@ -103,6 +104,14 @@ export function makePaintCursorRenderer<T extends HTMLCanvasElement | OffscreenC
103
104
  drawCtx.drawImage(canvas, Math.floor(dx), Math.floor(dy))
104
105
  }
105
106
 
107
+ function drawRaw(
108
+ drawCtx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
109
+ x: number,
110
+ y: number,
111
+ ) {
112
+ drawCtx.drawImage(canvas, Math.floor(x * _scale), Math.floor(y * _scale))
113
+ }
114
+
106
115
  function getSettings() {
107
116
  return {
108
117
  color: _color,
@@ -116,6 +125,7 @@ export function makePaintCursorRenderer<T extends HTMLCanvasElement | OffscreenC
116
125
  getBounds,
117
126
  getBoundsScaled: getOutlineBoundsScaled,
118
127
  draw,
128
+ drawRaw,
119
129
  getSettings,
120
130
  }
121
131
  }
@@ -1,32 +1,31 @@
1
- import type { PixelEngineConfig } from '../History/PixelEngineConfig'
2
1
  import type { Rect } from '../Rect/_rect-types'
3
- import type { Tile } from '../Tile/_tile-types'
2
+ import type { Tile, TileTargetMeta } from '../Tile/_tile-types'
4
3
  import type { TilePool } from '../Tile/TilePool'
5
4
 
6
5
  export function eachTileInBounds<T extends Tile>(
7
- config: PixelEngineConfig,
6
+ config: TileTargetMeta,
8
7
  lookup: (T | undefined)[],
9
8
  tilePool: TilePool<T>,
10
9
  bounds: Rect,
11
10
  callback: (tile: T, bX: number, bY: number, bW: number, bH: number) => void,
12
11
  ): void {
13
- const { tileShift, targetColumns, targetRows, tileSize } = config
12
+ const { targetRows, targetColumns, tileSize } = config
14
13
 
15
- const x1 = Math.max(0, bounds.x >> tileShift)
16
- const y1 = Math.max(0, bounds.y >> tileShift)
17
- const x2 = Math.min(targetColumns - 1, (bounds.x + bounds.w - 1) >> tileShift)
18
- const y2 = Math.min(targetRows - 1, (bounds.y + bounds.h - 1) >> tileShift)
14
+ const x1 = Math.max(0, Math.floor(bounds.x / tileSize))
15
+ const y1 = Math.max(0, Math.floor(bounds.y / tileSize))
16
+ const x2 = Math.min(targetColumns - 1, Math.floor((bounds.x + bounds.w - 1) / tileSize))
17
+ const y2 = Math.min(targetRows - 1, Math.floor((bounds.y + bounds.h - 1) / tileSize))
19
18
 
20
19
  if (x1 > x2 || y1 > y2) return
21
20
 
22
21
  for (let ty = y1; ty <= y2; ty++) {
23
22
  const rowOffset = ty * targetColumns
24
- const tileTop = ty << tileShift
23
+ const tileTop = ty * tileSize
25
24
 
26
25
  for (let tx = x1; tx <= x2; tx++) {
27
26
  const id = rowOffset + tx
28
27
  const tile = lookup[id] ?? (lookup[id] = tilePool.getTile(id, tx, ty))
29
- const tileLeft = tx << tileShift
28
+ const tileLeft = tx * tileSize
30
29
 
31
30
  const startX = bounds.x > tileLeft ? bounds.x : tileLeft
32
31
  const startY = bounds.y > tileTop ? bounds.y : tileTop
@@ -15,3 +15,10 @@ export interface MutablePixelData32 {
15
15
  export interface PixelData<T extends ImageDataLike = ImageData> extends PixelData32 {
16
16
  readonly imageData: T
17
17
  }
18
+
19
+ export interface MutablePixelData<T extends ImageDataLike = ImageData> extends PixelData32 {
20
+ imageData: T
21
+ data: Uint32Array
22
+ w: number
23
+ h: number
24
+ }
@@ -1,5 +1,6 @@
1
- import { type Color32, type ColorBlendOptions } from '../_types'
1
+ import { type ColorBlendOptions } from '../_types'
2
2
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
+ import type { Color32 } from '../Color/_color-types'
3
4
  import type { PixelData32 } from './_pixelData-types'
4
5
 
5
6
  /**
@@ -1,5 +1,6 @@
1
- import type { Color32, ColorBlendMaskOptions } from '../_types'
1
+ import type { ColorBlendMaskOptions } from '../_types'
2
2
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
+ import type { Color32 } from '../Color/_color-types'
3
4
  import type { AlphaMask } from '../Mask/_mask-types'
4
5
  import type { PixelData32 } from './_pixelData-types'
5
6
 
@@ -1,5 +1,6 @@
1
- import type { Color32, ColorBlendMaskOptions } from '../_types'
1
+ import type { ColorBlendMaskOptions } from '../_types'
2
2
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
+ import type { Color32 } from '../Color/_color-types'
3
4
  import type { BinaryMask } from '../Mask/_mask-types'
4
5
  import type { PixelData32 } from './_pixelData-types'
5
6
 
@@ -1,4 +1,5 @@
1
- import { type Color32, type ColorBlendMaskOptions } from '../_types'
1
+ import { type ColorBlendMaskOptions } from '../_types'
2
+ import type { Color32 } from '../Color/_color-types'
2
3
  import { type Mask, MaskType } from '../Mask/_mask-types'
3
4
  import type { PixelData32 } from './_pixelData-types'
4
5
  import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
@@ -1,5 +1,5 @@
1
- import { type Color32 } from '../_types'
2
1
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
2
+ import type { Color32 } from '../Color/_color-types'
3
3
  import type { PaintAlphaMask } from '../Paint/_paint-types'
4
4
  import type { PixelData32 } from './_pixelData-types'
5
5
  import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
@@ -1,5 +1,5 @@
1
- import { type Color32 } from '../_types'
2
1
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
2
+ import type { Color32 } from '../Color/_color-types'
3
3
  import type { PaintBinaryMask } from '../Paint/_paint-types'
4
4
  import type { PixelData32 } from './_pixelData-types'
5
5
  import { blendColorPixelDataBinaryMask } from './blendColorPixelDataBinaryMask'
@@ -1,8 +1,9 @@
1
- import { type Color32 } from '../_types'
2
1
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
2
+ import type { Color32 } from '../Color/_color-types'
3
3
  import { MaskType } from '../Mask/_mask-types'
4
- import type { PaintMask } from '../Paint/_paint-types'
4
+ import type { PaintMask, PaintRect } from '../Paint/_paint-types'
5
5
  import type { PixelData32 } from './_pixelData-types'
6
+ import { blendColorPixelData } from './blendColorPixelData'
6
7
  import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
7
8
  import { blendColorPixelDataBinaryMask } from './blendColorPixelDataBinaryMask'
8
9
 
@@ -11,12 +12,14 @@ const SCRATCH_OPTS = {
11
12
  y: 0,
12
13
  alpha: 255,
13
14
  blendFn: sourceOverPerfect,
15
+ w: undefined as number | undefined,
16
+ h: undefined as number | undefined,
14
17
  }
15
18
 
16
19
  export function blendColorPixelDataPaintMask(
17
- dst: PixelData32,
20
+ target: PixelData32,
18
21
  color: Color32,
19
- mask: PaintMask,
22
+ mask: PaintMask | PaintRect,
20
23
  x: number,
21
24
  y: number,
22
25
  alpha = 255,
@@ -29,10 +32,18 @@ export function blendColorPixelDataPaintMask(
29
32
  SCRATCH_OPTS.y = ty
30
33
  SCRATCH_OPTS.alpha = alpha
31
34
  SCRATCH_OPTS.blendFn = blendFn
35
+ SCRATCH_OPTS.w = undefined
36
+ SCRATCH_OPTS.h = undefined
32
37
 
33
- if (mask.type === MaskType.BINARY) {
34
- return blendColorPixelDataBinaryMask(dst, color, mask, SCRATCH_OPTS)
35
- } else {
36
- return blendColorPixelDataAlphaMask(dst, color, mask, SCRATCH_OPTS)
38
+ if (mask.data) {
39
+ if (mask.type === MaskType.BINARY) {
40
+ return blendColorPixelDataBinaryMask(target, color, mask, SCRATCH_OPTS)
41
+ } else {
42
+ return blendColorPixelDataAlphaMask(target, color, mask, SCRATCH_OPTS)
43
+ }
37
44
  }
45
+ SCRATCH_OPTS.w = mask.w
46
+ SCRATCH_OPTS.h = mask.h
47
+
48
+ return blendColorPixelData(target, color, SCRATCH_OPTS)
38
49
  }
@@ -1,5 +1,6 @@
1
- import type { BlendColor32, Color32 } from '../_types'
1
+ import type { BlendColor32 } from '../_types'
2
2
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
+ import type { Color32 } from '../Color/_color-types'
3
4
  import type { PixelData32 } from './_pixelData-types'
4
5
 
5
6
  export function blendPixel(
@@ -1,5 +1,6 @@
1
- import { type Color32, type PixelBlendOptions } from '../_types'
1
+ import { type PixelBlendOptions } from '../_types'
2
2
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
+ import type { Color32 } from '../Color/_color-types'
3
4
  import type { PixelData32 } from './_pixelData-types'
4
5
 
5
6
  /**
@@ -1,5 +1,6 @@
1
- import { type Color32, type PixelBlendMaskOptions } from '../_types'
1
+ import { type PixelBlendMaskOptions } from '../_types'
2
2
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
+ import type { Color32 } from '../Color/_color-types'
3
4
  import type { AlphaMask } from '../Mask/_mask-types'
4
5
  import type { PixelData32 } from './_pixelData-types'
5
6
 
@@ -1,5 +1,6 @@
1
- import type { Color32, PixelBlendMaskOptions } from '../_types'
1
+ import type { PixelBlendMaskOptions } from '../_types'
2
2
  import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
+ import type { Color32 } from '../Color/_color-types'
3
4
  import type { BinaryMask } from '../Mask/_mask-types'
4
5
  import type { PixelData32 } from './_pixelData-types'
5
6
 
@@ -17,14 +17,13 @@ export function blendPixelDataPaintBuffer(
17
17
  blendFn?: BlendColor32,
18
18
  blendPixelDataFn = blendPixelData,
19
19
  ): void {
20
- const tileShift = paintBuffer.config.tileShift
21
20
  const lookup = paintBuffer.lookup
22
21
  for (let i = 0; i < lookup.length; i++) {
23
22
  const tile = lookup[i]
24
23
 
25
24
  if (tile) {
26
- const x = tile.tx << tileShift
27
- const y = tile.ty << tileShift
25
+ const x = tile.x
26
+ const y = tile.y
28
27
 
29
28
  SCRATCH_OPTS.x = x
30
29
  SCRATCH_OPTS.y = y
@@ -1,4 +1,4 @@
1
- import type { Color32 } from '../_types'
1
+ import type { Color32 } from '../Color/_color-types'
2
2
  import type { BinaryMaskRect } from '../Mask/_mask-types'
3
3
  import type { PixelData32 } from './_pixelData-types'
4
4
  import { fillPixelDataFast } from './fillPixelDataFast'
@@ -0,0 +1,36 @@
1
+ import type { MutablePixelData, PixelData, PixelData32 } from './_pixelData-types'
2
+ import { setPixelData } from './PixelData'
3
+
4
+ export function cropPixelData(src: PixelData32, x: number, y: number, w: number, h: number, out?: MutablePixelData): PixelData {
5
+ const cx = Math.max(x, 0)
6
+ const cy = Math.max(y, 0)
7
+ const cw = Math.min(x + w, src.w) - cx
8
+ const ch = Math.min(y + h, src.h) - cy
9
+
10
+ if (cw <= 0 || ch <= 0) {
11
+ throw new Error(`Crop [${x},${y} ${w}x${h}] does not overlap PixelData [${src.w}x${src.h}]`)
12
+ }
13
+
14
+ const cropped = new ImageData(cw, ch)
15
+
16
+ let dst32: Uint32Array
17
+ if (out) {
18
+ setPixelData(out, cropped)
19
+ dst32 = out.data
20
+ } else {
21
+ dst32 = new Uint32Array(cropped.data.buffer)
22
+ }
23
+
24
+ for (let row = 0; row < ch; row++) {
25
+ const srcOffset = ((cy + row) * src.w) + cx
26
+ const dstOffset = row * cw
27
+ dst32.set(src.data.subarray(srcOffset, srcOffset + cw), dstOffset)
28
+ }
29
+
30
+ return out ?? {
31
+ data: dst32,
32
+ imageData: cropped,
33
+ w: cw,
34
+ h: ch,
35
+ }
36
+ }
@@ -1,4 +1,4 @@
1
- import type { Color32 } from '../_types'
1
+ import type { Color32 } from '../Color/_color-types'
2
2
  import type { Rect } from '../Rect/_rect-types'
3
3
  import type { PixelData32 } from './_pixelData-types'
4
4
 
@@ -47,16 +47,16 @@ export function fillPixelData(
47
47
  let w: number
48
48
  let h: number
49
49
 
50
- if (typeof _x === 'object') {
51
- x = _x.x ?? 0
52
- y = _x.y ?? 0
53
- w = _x.w ?? dstW
54
- h = _x.h ?? dstH
55
- } else if (typeof _x === 'number') {
50
+ if (typeof _x === 'number') {
56
51
  x = _x
57
52
  y = _y!
58
53
  w = _w!
59
54
  h = _h!
55
+ } else if (typeof _x === 'object') {
56
+ x = _x.x ?? 0
57
+ y = _x.y ?? 0
58
+ w = _x.w ?? dstW
59
+ h = _x.h ?? dstH
60
60
  } else {
61
61
  x = 0
62
62
  y = 0
@@ -1,4 +1,4 @@
1
- import type { Color32 } from '../_types'
1
+ import type { Color32 } from '../Color/_color-types'
2
2
  import type { BinaryMask } from '../Mask/_mask-types'
3
3
  import type { PixelData32 } from './_pixelData-types'
4
4
 
@@ -1,4 +1,4 @@
1
- import type { Color32 } from '../_types'
1
+ import type { Color32 } from '../Color/_color-types'
2
2
  import type { Rect } from '../Rect/_rect-types'
3
3
  import type { PixelData32 } from './_pixelData-types'
4
4