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
@@ -0,0 +1,49 @@
1
+ import type { Rect } from '../Rect/_rect-types'
2
+ import type { MutablePixelData, PixelData, PixelData32 } from './_pixelData-types'
3
+ import { cropPixelData } from './cropPixelData'
4
+
5
+ export function getPixelDataTransparentTrimmedBounds(target: PixelData32): Rect | null {
6
+ let minX = target.w
7
+ let minY = target.h
8
+ let maxX = -1
9
+ let maxY = -1
10
+
11
+ for (let y = 0; y < target.h; y++) {
12
+ for (let x = 0; x < target.w; x++) {
13
+ const alpha = target.data[y * target.w + x] >>> 24
14
+ if (alpha !== 0) {
15
+ if (x < minX) minX = x
16
+ if (x > maxX) maxX = x
17
+ if (y < minY) minY = y
18
+ if (y > maxY) maxY = y
19
+ }
20
+ }
21
+ }
22
+
23
+ if (maxX === -1) return null
24
+
25
+ return {
26
+ x: minX,
27
+ y: minY,
28
+ w: maxX - minX + 1,
29
+ h: maxY - minY + 1,
30
+ }
31
+ }
32
+
33
+ export function trimTransparentPixelData(target: PixelData32): PixelData {
34
+ const r = getPixelDataTransparentTrimmedBounds(target)
35
+ if (!r) {
36
+ throw new Error('PixelData is fully transparent — no crop bounds found')
37
+ }
38
+
39
+ return cropPixelData(target, r.x, r.y, r.w, r.h)
40
+ }
41
+
42
+ export function trimTransparentPixelDataInPlace(target: MutablePixelData) {
43
+ const r = getPixelDataTransparentTrimmedBounds(target)
44
+ if (!r) {
45
+ throw new Error('PixelData is fully transparent — no crop bounds found')
46
+ }
47
+
48
+ cropPixelData(target, r.x, r.y, r.w, r.h, target)
49
+ }
@@ -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'
@@ -152,6 +154,7 @@ export * from './PixelData/blendPixelDataMask'
152
154
  export * from './PixelData/blendPixelDataPaintBuffer'
153
155
  export * from './PixelData/clearPixelDataFast'
154
156
  export * from './PixelData/copyPixelData'
157
+ export * from './PixelData/cropPixelData'
155
158
  export * from './PixelData/extractPixelData'
156
159
  export * from './PixelData/extractPixelDataBuffer'
157
160
  export * from './PixelData/fillPixelData'
@@ -165,6 +168,7 @@ export * from './PixelData/resamplePixelData'
165
168
  export * from './PixelData/resizePixelData'
166
169
  export * from './PixelData/ReusablePixelData'
167
170
  export * from './PixelData/rotatePixelData'
171
+ export * from './PixelData/trimPixelData'
168
172
  export * from './PixelData/uInt32ArrayToPixelData'
169
173
  export * from './PixelData/writePaintBufferToPixelData'
170
174
  export * from './PixelData/writePixelData'
@@ -179,3 +183,5 @@ export * from './Tile/_tile-types'
179
183
  export * from './Tile/MaskTile'
180
184
  export * from './Tile/PixelTile'
181
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
- }