pixel-data-js 0.36.0 → 0.38.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 (73) hide show
  1. package/dist/index.prod.cjs +376 -305
  2. package/dist/index.prod.cjs.map +1 -1
  3. package/dist/index.prod.d.ts +109 -67
  4. package/dist/index.prod.js +372 -304
  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 +13 -15
  18. package/src/History/PixelMutator/mutatorBlendColor.ts +2 -1
  19. package/src/History/PixelMutator/mutatorBlendColorPaintAlphaMask.ts +1 -1
  20. package/src/History/PixelMutator/mutatorBlendColorPaintBinaryMask.ts +1 -1
  21. package/src/History/PixelMutator/mutatorBlendColorPaintMask.ts +23 -8
  22. package/src/History/PixelMutator/mutatorBlendColorPaintRect.ts +2 -1
  23. package/src/History/PixelMutator/mutatorBlendPixel.ts +2 -1
  24. package/src/History/PixelMutator/mutatorClear.ts +1 -1
  25. package/src/History/PixelMutator/mutatorFill.ts +1 -1
  26. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +1 -1
  27. package/src/History/PixelWriter.ts +5 -5
  28. package/src/ImageData/serialization.ts +7 -2
  29. package/src/IndexedImage/IndexedImage.ts +1 -1
  30. package/src/IndexedImage/indexedImageToAverageColor.ts +3 -2
  31. package/src/Mask/_mask-types.ts +9 -0
  32. package/src/Paint/AlphaMaskPaintBuffer.ts +26 -26
  33. package/src/Paint/BinaryMaskPaintBuffer.ts +19 -19
  34. package/src/Paint/ColorPaintBuffer.ts +40 -42
  35. package/src/Paint/Commit/AlphaMaskPaintBufferCommitter.ts +1 -1
  36. package/src/Paint/Commit/AlphaMaskPaintBufferManager.ts +6 -7
  37. package/src/Paint/Commit/BinaryMaskPaintBufferCommitter.ts +1 -1
  38. package/src/Paint/Commit/BinaryMaskPaintBufferManager.ts +6 -7
  39. package/src/Paint/Commit/ColorPaintBufferManager.ts +6 -7
  40. package/src/Paint/Commit/commitColorPaintBuffer.ts +2 -6
  41. package/src/Paint/Commit/commitMaskPaintBuffer.ts +3 -7
  42. package/src/Paint/Render/AlphaMaskPaintBufferCanvasRenderer.ts +42 -25
  43. package/src/Paint/Render/BinaryMaskPaintBufferCanvasRenderer.ts +40 -24
  44. package/src/Paint/Render/ColorPaintBufferCanvasRenderer.ts +21 -21
  45. package/src/Paint/Render/PaintCursorRenderer.ts +3 -2
  46. package/src/Paint/eachTileInBounds.ts +9 -10
  47. package/src/PixelData/blendColorPixelData.ts +2 -1
  48. package/src/PixelData/blendColorPixelDataAlphaMask.ts +2 -1
  49. package/src/PixelData/blendColorPixelDataBinaryMask.ts +2 -1
  50. package/src/PixelData/blendColorPixelDataMask.ts +2 -1
  51. package/src/PixelData/blendColorPixelDataPaintAlphaMask.ts +1 -1
  52. package/src/PixelData/blendColorPixelDataPaintBinaryMask.ts +1 -1
  53. package/src/PixelData/blendColorPixelDataPaintMask.ts +19 -8
  54. package/src/PixelData/blendPixel.ts +2 -1
  55. package/src/PixelData/blendPixelData.ts +2 -1
  56. package/src/PixelData/blendPixelDataAlphaMask.ts +2 -1
  57. package/src/PixelData/blendPixelDataBinaryMask.ts +2 -1
  58. package/src/PixelData/blendPixelDataPaintBuffer.ts +2 -3
  59. package/src/PixelData/clearPixelDataFast.ts +1 -1
  60. package/src/PixelData/fillPixelData.ts +1 -1
  61. package/src/PixelData/fillPixelDataBinaryMask.ts +1 -1
  62. package/src/PixelData/fillPixelDataFast.ts +1 -1
  63. package/src/PixelData/writePaintBufferToPixelData.ts +1 -5
  64. package/src/Tile/MaskTile.ts +4 -0
  65. package/src/Tile/PixelTile.ts +2 -0
  66. package/src/Tile/TilePool.ts +9 -8
  67. package/src/Tile/TileTargetConfig.ts +27 -0
  68. package/src/Tile/_tile-types.ts +16 -0
  69. package/src/_types.ts +1 -6
  70. package/src/index.ts +7 -3
  71. package/src/History/PixelEngineConfig.ts +0 -28
  72. package/src/Internal/_constants.ts +0 -3
  73. package/src/color.ts +0 -112
@@ -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'
@@ -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
@@ -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'
@@ -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
 
@@ -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
 
@@ -10,17 +10,13 @@ export function writePaintBufferToPixelData(
10
10
  paintBuffer: ColorPaintBuffer,
11
11
  writePixelDataBufferFn = writePixelDataBuffer,
12
12
  ) {
13
- const tileShift = paintBuffer.config.tileShift
14
13
  const lookup = paintBuffer.lookup
15
14
 
16
15
  for (let i = 0; i < lookup.length; i++) {
17
16
  const tile = lookup[i]
18
17
 
19
18
  if (tile) {
20
- const dx = tile.tx << tileShift
21
- const dy = tile.ty << tileShift
22
-
23
- writePixelDataBufferFn(target, tile.data, dx, dy, tile.w, tile.h)
19
+ writePixelDataBufferFn(target, tile.data, tile.x, tile.y, tile.w, tile.h)
24
20
  }
25
21
  }
26
22
  }
@@ -14,6 +14,8 @@ export const makeAlphaMaskTile: TileFactory<AlphaMaskTile> = (
14
14
  data: new Uint8Array(tileArea),
15
15
  w: tileSize,
16
16
  h: tileSize,
17
+ x: tx * tileSize,
18
+ y: ty * tileSize,
17
19
  id,
18
20
  tx,
19
21
  ty,
@@ -33,6 +35,8 @@ export const makeBinaryMaskTile: TileFactory<BinaryMaskTile> = (
33
35
  data: new Uint8Array(tileArea),
34
36
  w: tileSize,
35
37
  h: tileSize,
38
+ x: tx * tileSize,
39
+ y: ty * tileSize,
36
40
  id,
37
41
  tx,
38
42
  ty,
@@ -15,6 +15,8 @@ export function makePixelTile(
15
15
  id,
16
16
  tx,
17
17
  ty,
18
+ x: tx * tileSize,
19
+ y: ty * tileSize,
18
20
  w: tileSize,
19
21
  h: tileSize,
20
22
  data: data32,
@@ -1,19 +1,17 @@
1
- import type { PixelEngineConfig } from '../History/PixelEngineConfig'
2
1
  import type { Tile, TileFactory } from './_tile-types'
3
2
 
4
3
  export class TilePool<T extends Tile> {
5
4
  public pool: T[]
6
5
 
7
- private tileSize: number
8
- private tileArea: number
6
+ protected tileArea: number
9
7
 
10
8
  constructor(
11
- config: PixelEngineConfig,
12
- private tileFactory: TileFactory<T>,
9
+ protected tileSize: number,
10
+ protected tileFactory: TileFactory<T>,
13
11
  ) {
14
12
  this.pool = []
15
- this.tileSize = config.tileSize
16
- this.tileArea = config.tileArea
13
+ this.tileSize = tileSize
14
+ this.tileArea = tileSize * tileSize
17
15
  }
18
16
 
19
17
  getTile(
@@ -22,11 +20,14 @@ export class TilePool<T extends Tile> {
22
20
  ty: number,
23
21
  ): T {
24
22
  let tile = this.pool.pop()
23
+ const tileSize = this.tileSize
25
24
 
26
25
  if (tile) {
27
26
  tile.id = id
28
27
  tile.tx = tx
29
28
  tile.ty = ty
29
+ tile.x = tx * tileSize
30
+ tile.y = ty * tileSize
30
31
 
31
32
  // Wipe dirty memory from previous uses before handing it out
32
33
  tile.data.fill(0)
@@ -38,7 +39,7 @@ export class TilePool<T extends Tile> {
38
39
  id,
39
40
  tx,
40
41
  ty,
41
- this.tileSize,
42
+ tileSize,
42
43
  this.tileArea,
43
44
  )
44
45
  }
@@ -0,0 +1,27 @@
1
+ import type { PixelData } from '../PixelData/_pixelData-types'
2
+ import type { TileTargetConfig, TileTargetMeta } from './_tile-types'
3
+
4
+ export function makeTileTargetConfig(
5
+ tileSize: number,
6
+ target: PixelData,
7
+ ): TileTargetConfig {
8
+ return {
9
+ target,
10
+ ...makeTileTargetMeta(tileSize, target),
11
+ }
12
+ }
13
+
14
+ export function makeTileTargetMeta(
15
+ tileSize: number,
16
+ target: { w: number, h: number },
17
+ ): TileTargetMeta {
18
+ return {
19
+ targetWidth: target.w,
20
+ targetHeight: target.h,
21
+ tileSize: tileSize,
22
+ invTileSize: 1 / tileSize,
23
+ tileArea: tileSize * tileSize,
24
+ targetColumns: Math.ceil(target.w / tileSize),
25
+ targetRows: Math.ceil(target.h / tileSize),
26
+ }
27
+ }
@@ -11,6 +11,8 @@ interface BaseTile {
11
11
  id: number
12
12
  tx: number
13
13
  ty: number
14
+ x: number
15
+ y: number
14
16
  }
15
17
 
16
18
  export interface PixelTile extends PixelData, BaseTile {
@@ -31,3 +33,17 @@ export type TileFactory<T extends Tile> = (
31
33
  tileSize: number,
32
34
  tileArea: number,
33
35
  ) => T
36
+
37
+ export interface TileTargetMeta {
38
+ readonly tileArea: number
39
+ readonly targetColumns: number
40
+ readonly targetRows: number
41
+ readonly targetWidth: number
42
+ readonly targetHeight: number
43
+ readonly tileSize: number
44
+ readonly invTileSize: number
45
+ }
46
+
47
+ export interface TileTargetConfig extends TileTargetMeta {
48
+ readonly target: PixelData
49
+ }
package/src/_types.ts CHANGED
@@ -1,11 +1,6 @@
1
+ import type { Color32 } from './Color/_color-types'
1
2
  import type { BinaryMask } from './Mask/_mask-types'
2
3
 
3
- /** ALL values are 0-255 (including alpha which in CSS is 0-1) */
4
- export type RGBA = { r: number, g: number, b: number, a: number }
5
-
6
- /** Represents a 32-bit color in 0xAABBGGRR (Little endian) */
7
- export type Color32 = number & { readonly __brandColor32: unique symbol }
8
-
9
4
  /**
10
5
  * A function that defines how to combine a source color with a destination color.
11
6
  * @param src - The incoming color (source).
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- // generated by `npm run re-index`
1
+ // generated by `npm run re-index`
2
2
 
3
3
  export * from './_errors'
4
4
  export * from './_types'
@@ -24,7 +24,10 @@ export * from './Clipboard/getImageDataFromClipboard'
24
24
  export * from './Clipboard/writeImageDataToClipboard'
25
25
  export * from './Clipboard/writeImgBlobToClipboard'
26
26
 
27
- export * from './color'
27
+ export * from './Color/_color-types'
28
+ export * from './Color/lerpColor32'
29
+ export * from './Color/convert-color'
30
+ export * from './Color/pack-color'
28
31
 
29
32
  export * from './Control/BatchedQueue'
30
33
  export * from './Control/RenderQueue'
@@ -32,7 +35,6 @@ export * from './Control/RenderQueue'
32
35
  export * from './History/HistoryAction'
33
36
  export * from './History/HistoryManager'
34
37
  export * from './History/PixelAccumulator'
35
- export * from './History/PixelEngineConfig'
36
38
  export * from './History/PixelMutator'
37
39
 
38
40
  export * from './History/PixelMutator/mutatorApplyAlphaMask'
@@ -181,3 +183,5 @@ export * from './Tile/_tile-types'
181
183
  export * from './Tile/MaskTile'
182
184
  export * from './Tile/PixelTile'
183
185
  export * from './Tile/TilePool'
186
+ export * from './Tile/TileTargetConfig'
187
+ export { colorDistance } from './Color/colorDistance'
@@ -1,28 +0,0 @@
1
- import type { PixelData } from '../PixelData/_pixelData-types'
2
-
3
- export class PixelEngineConfig {
4
- readonly tileSize: number
5
- // pixelX = tileX << tileShift
6
- // pixelY = tileY << tileShift
7
- readonly tileShift: number
8
- readonly tileMask: number
9
- readonly tileArea: number
10
- readonly target!: PixelData
11
- readonly targetColumns: number = 0
12
- readonly targetRows: number = 0
13
-
14
- constructor(tileSize: number, target: PixelData) {
15
- // Ensure it's a power of 2 to guarantee bitwise math works
16
- if ((tileSize & (tileSize - 1)) !== 0) {
17
- throw new Error('tileSize must be a power of 2')
18
- }
19
-
20
- this.tileSize = tileSize
21
- this.tileShift = 31 - Math.clz32(tileSize)
22
- this.tileMask = tileSize - 1
23
- this.tileArea = tileSize * tileSize
24
- this.target = target
25
- this.targetColumns = (target.w + this.tileMask) >> this.tileShift
26
- this.targetRows = (target.h + this.tileMask) >> this.tileShift
27
- }
28
- }
@@ -1,3 +0,0 @@
1
- import type { CanvasObjectFactory } from '../Canvas/_canvas-types'
2
-
3
- export const DEFAULT_CANVAS_FACTORY: CanvasObjectFactory<any> = (w, h) => new OffscreenCanvas(w, h)
package/src/color.ts DELETED
@@ -1,112 +0,0 @@
1
- import type { Color32, RGBA } from './_types'
2
-
3
- /**
4
- * Packs RGBA into a 32-bit integer compatible with
5
- * Little-Endian Uint32Array views on ImageData.
6
- */
7
- export function packColor(r: number, g: number, b: number, a: number): Color32 {
8
- return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
9
- }
10
-
11
- export function packRGBA({ r, g, b, a }: RGBA): Color32 {
12
- return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
13
- }
14
-
15
- export const unpackRed = (packed: Color32): number => (packed >>> 0) & 0xFF
16
- export const unpackGreen = (packed: Color32): number => (packed >>> 8) & 0xFF
17
- export const unpackBlue = (packed: Color32): number => (packed >>> 16) & 0xFF
18
- export const unpackAlpha = (packed: Color32): number => (packed >>> 24) & 0xFF
19
-
20
- export function unpackColor(packed: Color32): RGBA {
21
- return {
22
- r: (packed >>> 0) & 0xFF,
23
- g: (packed >>> 8) & 0xFF,
24
- b: (packed >>> 16) & 0xFF,
25
- a: (packed >>> 24) & 0xFF,
26
- }
27
- }
28
-
29
- const SCRATCH_RGBA: RGBA = { r: 0, g: 0, b: 0, a: 0 }
30
-
31
- // uses a scratch arg for memory perf. be careful about re-use.
32
- export function unpackColorTo(packed: Color32, scratch = SCRATCH_RGBA): RGBA {
33
- scratch.r = (packed >>> 0) & 0xFF
34
- scratch.g = (packed >>> 8) & 0xFF
35
- scratch.b = (packed >>> 16) & 0xFF
36
- scratch.a = (packed >>> 24) & 0xFF
37
- return scratch
38
- }
39
-
40
- export function colorDistance(a: Color32, b: Color32): number {
41
- const dr = (a & 0xFF) - (b & 0xFF)
42
- const dg = ((a >>> 8) & 0xFF) - ((b >>> 8) & 0xFF)
43
- const db = ((a >>> 16) & 0xFF) - ((b >>> 16) & 0xFF)
44
- const da = ((a >>> 24) & 0xFF) - ((b >>> 24) & 0xFF)
45
- return dr * dr + dg * dg + db * db + da * da
46
- }
47
-
48
- /**
49
- * Linearly interpolates between two 32-bit colors using a floating-point weight.
50
- * * This is the preferred method for UI animations or scenarios where high
51
- * precision is required. It uses the standard `a + t * (b - a)` formula
52
- * for each channel.
53
- * @param a - The starting color as a 32-bit integer (AABBGGRR).
54
- * @param b - The target color as a 32-bit integer (AABBGGRR).
55
- * @param t - The interpolation factor between 0.0 and 1.0.
56
- * @returns The interpolated 32-bit color.
57
- */
58
- export function lerpColor32(a: Color32, b: Color32, t: number): Color32 {
59
- const r = (a & 0xFF) + t * ((b & 0xFF) - (a & 0xFF))
60
- const g = ((a >>> 8) & 0xFF) + t * (((b >>> 8) & 0xFF) - ((a >>> 8) & 0xFF))
61
- const b_ = ((a >>> 16) & 0xFF) + t * (((b >>> 16) & 0xFF) - ((a >>> 16) & 0xFF))
62
- const a_ = ((a >>> 24) & 0xFF) + t * (((b >>> 24) & 0xFF) - ((a >>> 24) & 0xFF))
63
-
64
- return ((a_ << 24) | (b_ << 16) | (g << 8) | r) >>> 0 as Color32
65
- }
66
-
67
- /**
68
- * Linearly interpolates between two 32-bit colors using integer fixed-point math.
69
- * Highly optimized for image processing and real-time blitting. It processes
70
- * channels in parallel using bitmasks (RB and GA pairs).
71
- * **Note:** Subject to a 1-bit drift (rounding down) due to fast bit-shift division.
72
- * @param src - The source (foreground) color as a 32-bit integer.
73
- * @param dst - The destination (background) color as a 32-bit integer.
74
- * @param w - The blend weight as a byte value from 0 to 255. Where 0 is 100% dst and 255 is 100% src
75
- * @returns The blended 32-bit color.
76
- */export function lerpColor32Fast(src: Color32, dst: Color32, w: number): Color32 {
77
- const invA = 255 - w
78
-
79
- // Masking Red and Blue: 0x00FF00FF
80
- // We process R and B in one go, then shift back down
81
- const rb = (((src & 0x00FF00FF) * w + (dst & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF
82
-
83
- // Masking Green and Alpha: 0xFF00FF00
84
- // We shift down first to avoid overflow, then shift back up
85
- const ga = ((((src >>> 8) & 0x00FF00FF) * w + ((dst >>> 8) & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF
86
-
87
- return (rb | (ga << 8)) >>> 0 as Color32
88
- }
89
-
90
- // Convert 0xAABBGGRR to #RRGGBBAA
91
- export function color32ToHex(color: Color32): string {
92
- const r = (color & 0xFF).toString(16).padStart(2, '0')
93
- const g = ((color >>> 8) & 0xFF).toString(16).padStart(2, '0')
94
- const b = ((color >>> 16) & 0xFF).toString(16).padStart(2, '0')
95
- const a = ((color >>> 24) & 0xFF).toString(16).padStart(2, '0')
96
- return `#${r}${g}${b}${a}`
97
- }
98
-
99
- /**
100
- * Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.
101
- * Example: 0xFF0000FF -> "rgba(255,0,0,1)"
102
- */
103
- export function color32ToCssRGBA(color: Color32): string {
104
- const r = color & 0xFF
105
- const g = (color >>> 8) & 0xFF
106
- const b = (color >>> 16) & 0xFF
107
- const a = (color >>> 24) & 0xFF
108
-
109
- const alpha = Number((a / 255).toFixed(3))
110
-
111
- return `rgba(${r},${g},${b},${alpha})`
112
- }