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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pixel-data-js",
3
3
  "type": "module",
4
- "version": "0.35.0",
4
+ "version": "0.37.0",
5
5
  "packageManager": "pnpm@10.33.0",
6
6
  "description": "JS Pixel and ImageData operations",
7
7
  "author": {
@@ -88,7 +88,7 @@
88
88
  "typedoc-plugin-mdn-links": "^5.1.1",
89
89
  "typedoc-rhineai-theme": "^1.2.0",
90
90
  "typescript": "^5.9.3",
91
- "unplugin-inline": "^1.15.0",
91
+ "unplugin-inline": "^1.16.0",
92
92
  "vite-tsconfig-paths": "^6.1.1",
93
93
  "vitest": "3.2.4"
94
94
  },
@@ -1,5 +1,6 @@
1
- import { type Color32 } from '../_types'
2
- import { colorDistance } from '../color'
1
+ import type { Color32 } from '../Color/_color-types'
2
+
3
+ import { colorDistance } from '../Color/colorDistance'
3
4
  import { extractImageDataBuffer } from '../ImageData/extractImageDataBuffer'
4
5
  import { type BinaryMaskRect, MaskType } from '../Mask/_mask-types'
5
6
  import type { PixelData } from '../PixelData/_pixelData-types'
@@ -1,4 +1,5 @@
1
- import type { BlendColor32, Color32 } from '../_types'
1
+ import type { BlendColor32 } from '../_types'
2
+ import type { Color32 } from '../Color/_color-types'
2
3
  import { BaseBlendMode, overwriteBase } from './blend-modes'
3
4
  import { makeBlendModeRegistry } from './BlendModeRegistry'
4
5
 
@@ -1,4 +1,5 @@
1
- import type { BlendColor32, Color32 } from '../_types'
1
+ import type { BlendColor32 } from '../_types'
2
+ import type { Color32 } from '../Color/_color-types'
2
3
  import { BaseBlendMode, overwriteBase } from './blend-modes'
3
4
  import { makeBlendModeRegistry } from './BlendModeRegistry'
4
5
 
@@ -63,11 +63,6 @@ function makeReusableCanvasMeta<T extends HTMLCanvasElement | OffscreenCanvas>(
63
63
  canvas.width = width
64
64
  canvas.height = height
65
65
  ctx!.imageSmoothingEnabled = false
66
- } else {
67
- // Always reset transform before clearing to ensure the whole buffer is wiped
68
- ctx!.setTransform(1, 0, 0, 1, 0, 0)
69
- // Same size → manually clear
70
- ctx!.clearRect(0, 0, width, height)
71
66
  }
72
67
 
73
68
  return result
@@ -0,0 +1,8 @@
1
+ /** Represents a 32-bit color in 0xAABBGGRR (Little endian) */
2
+ export type Color32 = number & { readonly __brandColor32: unique symbol }
3
+
4
+ /** ALL values are 0-255 */
5
+ export type RGBA = { r: number, g: number, b: number, a: number }
6
+
7
+ /** r, g, b are 0-255. a is 0-1 for css use */
8
+ export type CssRGBA = { r: number, g: number, b: number, a: number } & { readonly __brandCssRGBA: unique symbol }
@@ -0,0 +1,9 @@
1
+ import type { Color32 } from './_color-types'
2
+
3
+ export function colorDistance(a: Color32, b: Color32): number {
4
+ const dr = (a & 0xFF) - (b & 0xFF)
5
+ const dg = ((a >>> 8) & 0xFF) - ((b >>> 8) & 0xFF)
6
+ const db = ((a >>> 16) & 0xFF) - ((b >>> 16) & 0xFF)
7
+ const da = ((a >>> 24) & 0xFF) - ((b >>> 24) & 0xFF)
8
+ return dr * dr + dg * dg + db * db + da * da
9
+ }
@@ -0,0 +1,43 @@
1
+ // Convert 0xAABBGGRR to #RRGGBBAA
2
+ import type { Color32, CssRGBA } from './_color-types'
3
+
4
+ export function color32ToHex(color: Color32): string {
5
+ const r = (color & 0xFF).toString(16).padStart(2, '0')
6
+ const g = ((color >>> 8) & 0xFF).toString(16).padStart(2, '0')
7
+ const b = ((color >>> 16) & 0xFF).toString(16).padStart(2, '0')
8
+ const a = ((color >>> 24) & 0xFF).toString(16).padStart(2, '0')
9
+ return `#${r}${g}${b}${a}`
10
+ }
11
+
12
+ /**
13
+ * Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.
14
+ * Example: 0xFF0000FF -> "rgba(255,0,0,1)"
15
+ */
16
+ export function color32ToCssRGBAString(color: Color32): string {
17
+ const r = color & 0xFF
18
+ const g = (color >>> 8) & 0xFF
19
+ const b = (color >>> 16) & 0xFF
20
+ const a = (color >>> 24) & 0xFF
21
+
22
+ const alpha = Number((a / 255).toFixed(3))
23
+
24
+ return `rgba(${r},${g},${b},${alpha})`
25
+ }
26
+
27
+ export function color32ToCssRGBA(color: Color32): CssRGBA {
28
+ const r = color & 0xFF
29
+ const g = (color >>> 8) & 0xFF
30
+ const b = (color >>> 16) & 0xFF
31
+ const a = (color >>> 24) & 0xFF
32
+
33
+ return {
34
+ r,
35
+ g,
36
+ b,
37
+ a: a / 255,
38
+ } as CssRGBA
39
+ }
40
+
41
+ export function cssRGBAToColor32({ r, g, b, a }: CssRGBA): Color32 {
42
+ return (((a * 255) << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
43
+ }
@@ -0,0 +1,44 @@
1
+ import type { Color32 } from './_color-types'
2
+
3
+ /**
4
+ * Linearly interpolates between two 32-bit colors using a floating-point weight.
5
+ * * This is the preferred method for UI animations or scenarios where high
6
+ * precision is required. It uses the standard `a + t * (b - a)` formula
7
+ * for each channel.
8
+ * @param a - The starting color as a 32-bit integer (AABBGGRR).
9
+ * @param b - The target color as a 32-bit integer (AABBGGRR).
10
+ * @param t - The interpolation factor between 0.0 and 1.0.
11
+ * @returns The interpolated 32-bit color.
12
+ */
13
+ export function lerpColor32(a: Color32, b: Color32, t: number): Color32 {
14
+ const r = (a & 0xFF) + t * ((b & 0xFF) - (a & 0xFF))
15
+ const g = ((a >>> 8) & 0xFF) + t * (((b >>> 8) & 0xFF) - ((a >>> 8) & 0xFF))
16
+ const b_ = ((a >>> 16) & 0xFF) + t * (((b >>> 16) & 0xFF) - ((a >>> 16) & 0xFF))
17
+ const a_ = ((a >>> 24) & 0xFF) + t * (((b >>> 24) & 0xFF) - ((a >>> 24) & 0xFF))
18
+
19
+ return ((a_ << 24) | (b_ << 16) | (g << 8) | r) >>> 0 as Color32
20
+ }
21
+
22
+ /**
23
+ * Linearly interpolates between two 32-bit colors using integer fixed-point math.
24
+ * Highly optimized for image processing and real-time blitting. It processes
25
+ * channels in parallel using bitmasks (RB and GA pairs).
26
+ * **Note:** Subject to a 1-bit drift (rounding down) due to fast bit-shift division.
27
+ * @param src - The source (foreground) color as a 32-bit integer.
28
+ * @param dst - The destination (background) color as a 32-bit integer.
29
+ * @param w - The blend weight as a byte value from 0 to 255. Where 0 is 100% dst and 255 is 100% src
30
+ * @returns The blended 32-bit color.
31
+ */
32
+ export function lerpColor32Fast(src: Color32, dst: Color32, w: number): Color32 {
33
+ const invA = 255 - w
34
+
35
+ // Masking Red and Blue: 0x00FF00FF
36
+ // We process R and B in one go, then shift back down
37
+ const rb = (((src & 0x00FF00FF) * w + (dst & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF
38
+
39
+ // Masking Green and Alpha: 0xFF00FF00
40
+ // We shift down first to avoid overflow, then shift back up
41
+ const ga = ((((src >>> 8) & 0x00FF00FF) * w + ((dst >>> 8) & 0x00FF00FF) * invA) >>> 8) & 0x00FF00FF
42
+
43
+ return (rb | (ga << 8)) >>> 0 as Color32
44
+ }
@@ -0,0 +1,38 @@
1
+ import type { Color32, RGBA } from './_color-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
+ }
@@ -1,5 +1,5 @@
1
+ import type { TileTargetConfig } from '../Tile/_tile-types'
1
2
  import type { PixelAccumulator } from './PixelAccumulator'
2
- import type { PixelEngineConfig } from './PixelEngineConfig'
3
3
  import { applyPatchTiles, type PixelPatchTiles } from './PixelPatchTiles'
4
4
 
5
5
  export interface HistoryAction {
@@ -11,7 +11,7 @@ export interface HistoryAction {
11
11
  export type HistoryActionFactory = typeof makeHistoryAction
12
12
 
13
13
  export function makeHistoryAction(
14
- config: PixelEngineConfig,
14
+ config: TileTargetConfig,
15
15
  accumulator: PixelAccumulator,
16
16
  patch: PixelPatchTiles,
17
17
  afterUndo?: (patch: PixelPatchTiles) => void,
@@ -1,6 +1,5 @@
1
- import type { PixelTile } from '../Tile/_tile-types'
1
+ import type { PixelTile, TileTargetConfig } from '../Tile/_tile-types'
2
2
  import type { TilePool } from '../Tile/TilePool'
3
- import type { PixelEngineConfig } from './PixelEngineConfig'
4
3
  import { applyPatchTiles, type PixelPatchTiles } from './PixelPatchTiles'
5
4
 
6
5
  export type DidChangeFn = (didChange: boolean) => boolean
@@ -10,7 +9,7 @@ export class PixelAccumulator {
10
9
  public beforeTiles: PixelTile[]
11
10
 
12
11
  constructor(
13
- readonly config: PixelEngineConfig,
12
+ readonly config: TileTargetConfig,
14
13
  readonly pixelTilePool: TilePool<PixelTile>,
15
14
  ) {
16
15
  this.lookup = []
@@ -26,11 +25,18 @@ export class PixelAccumulator {
26
25
  * @param x pixel x coordinate
27
26
  * @param y pixel y coordinate
28
27
  */
29
- storePixelBeforeState(x: number, y: number): DidChangeFn {
30
- const shift = this.config.tileShift
28
+ storePixelBeforeState(x: number, y: number): DidChangeFn | null {
31
29
  const columns = this.config.targetColumns
32
- const tx = x >> shift
33
- const ty = y >> shift
30
+ const targetWidth = this.config.targetWidth
31
+ const targetHeight = this.config.targetHeight
32
+
33
+ // Return a no-op if the pixel is outside the target boundaries
34
+ if (x < 0 || x >= targetWidth || y < 0 || y >= targetHeight) {
35
+ return null
36
+ }
37
+
38
+ const tx = (x * this.config.invTileSize) | 0
39
+ const ty = (y * this.config.invTileSize) | 0
34
40
  const id = ty * columns + tx
35
41
 
36
42
  let tile = this.lookup[id]
@@ -66,14 +72,27 @@ export class PixelAccumulator {
66
72
  y: number,
67
73
  w: number,
68
74
  h: number,
69
- ): DidChangeFn {
70
- const shift = this.config.tileShift
75
+ ): DidChangeFn | null {
71
76
  const columns = this.config.targetColumns
77
+ const targetWidth = this.config.targetWidth
78
+ const targetHeight = this.config.targetHeight
79
+ const invTileSize = this.config.invTileSize
80
+
81
+ // Clamp the bounding box to the actual canvas dimensions
82
+ const clipX1 = Math.max(0, x)
83
+ const clipY1 = Math.max(0, y)
84
+ const clipX2 = Math.min(targetWidth - 1, x + w - 1)
85
+ const clipY2 = Math.min(targetHeight - 1, y + h - 1)
86
+
87
+ // If the region is entirely off-canvas, return a no-op
88
+ if (clipX2 < clipX1 || clipY2 < clipY1) {
89
+ return null
90
+ }
72
91
 
73
- const startX = x >> shift
74
- const startY = y >> shift
75
- const endX = (x + w - 1) >> shift
76
- const endY = (y + h - 1) >> shift
92
+ const startX = (clipX1 * invTileSize) | 0
93
+ const startY = (clipY1 * invTileSize) | 0
94
+ const endX = (clipX2 * invTileSize) | 0
95
+ const endY = (clipY2 * invTileSize) | 0
77
96
 
78
97
  const startIndex = this.beforeTiles.length
79
98
 
@@ -26,6 +26,8 @@ export const mutatorApplyAlphaMask = ((writer: PixelWriter<any>, deps: Deps = de
26
26
  const h = opts?.h ?? target.h
27
27
 
28
28
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
29
+ if (!didChange) return false
30
+
29
31
  return didChange(applyAlphaMaskToPixelData(target, mask, opts))
30
32
  },
31
33
  }
@@ -26,7 +26,11 @@ export const mutatorApplyBinaryMask = ((writer: PixelWriter<any>, deps: Deps = d
26
26
  const h = opts?.h ?? target.h
27
27
 
28
28
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
29
- return didChange(applyBinaryMaskToPixelData(target, mask, opts))
29
+ if (!didChange) return false
30
+
31
+ const b = applyBinaryMaskToPixelData(target, mask, opts)
32
+ console.log({ b })
33
+ return didChange(b)
30
34
  },
31
35
  }
32
36
  }) satisfies HistoryMutator<any, Deps>
@@ -29,6 +29,7 @@ export const mutatorApplyMask = ((writer: PixelWriter<any>, deps: Deps = default
29
29
  const h = opts?.h ?? target.h
30
30
 
31
31
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
32
+ if (!didChange) return false
32
33
 
33
34
  if (mask.type === MaskType.BINARY) {
34
35
  return didChange(applyBinaryMaskToPixelData(target, mask, opts))
@@ -27,6 +27,7 @@ export const mutatorBlendAlphaMask = ((writer: PixelWriter<any>, deps: Partial<D
27
27
  const h = opts?.h ?? src.h
28
28
 
29
29
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
30
+ if (!didChange) return false
30
31
 
31
32
  return didChange(
32
33
  blendPixelDataAlphaMask(writer.config.target, src, mask, opts),
@@ -27,6 +27,7 @@ export const mutatorBlendBinaryMask = ((writer: PixelWriter<any>, deps: Partial<
27
27
  const h = opts?.h ?? src.h
28
28
 
29
29
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
30
+ if (!didChange) return false
30
31
 
31
32
  return didChange(
32
33
  blendPixelDataBinaryMask(writer.config.target, src, mask, opts),
@@ -1,4 +1,5 @@
1
- import type { Color32, ColorBlendOptions } from '../../_types'
1
+ import type { ColorBlendOptions } from '../../_types'
2
+ import type { Color32 } from '../../Color/_color-types'
2
3
  import { blendColorPixelData } from '../../PixelData/blendColorPixelData'
3
4
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
4
5
 
@@ -25,6 +26,8 @@ export const mutatorBlendColor = ((writer: PixelWriter<any>, deps: Deps = defaul
25
26
  const h = opts?.h ?? target.h
26
27
 
27
28
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
29
+ if (!didChange) return false
30
+
28
31
  return didChange(
29
32
  blendColorPixelData(target, color, opts),
30
33
  )
@@ -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 { blendColorPixelDataAlphaMask } from '../../PixelData/blendColorPixelDataAlphaMask'
5
5
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
@@ -37,6 +37,7 @@ export const mutatorBlendColorPaintAlphaMask = ((writer: PixelWriter<any>, deps:
37
37
  const ty = y + mask.centerOffsetY
38
38
 
39
39
  const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h)
40
+ if (!didChange) return false
40
41
 
41
42
  OPTS.x = tx
42
43
  OPTS.y = ty
@@ -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 { blendColorPixelDataBinaryMask } from '../../PixelData/blendColorPixelDataBinaryMask'
5
5
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
@@ -37,6 +37,7 @@ export const mutatorBlendColorPaintBinaryMask = ((writer: PixelWriter<any>, deps
37
37
  const ty = y + mask.centerOffsetY
38
38
 
39
39
  const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h)
40
+ if (!didChange) return false
40
41
 
41
42
  OPTS.x = tx
42
43
  OPTS.y = ty
@@ -1,7 +1,8 @@
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
+ import { blendColorPixelData } from '../../PixelData/blendColorPixelData'
5
6
  import { blendColorPixelDataAlphaMask } from '../../PixelData/blendColorPixelDataAlphaMask'
6
7
  import { blendColorPixelDataBinaryMask } from '../../PixelData/blendColorPixelDataBinaryMask'
7
8
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
@@ -9,6 +10,7 @@ import { type HistoryMutator, PixelWriter } from '../PixelWriter'
9
10
  const defaults = {
10
11
  blendColorPixelDataAlphaMask,
11
12
  blendColorPixelDataBinaryMask,
13
+ blendColorPixelData,
12
14
  }
13
15
  type Deps = Partial<typeof defaults>
14
16
 
@@ -19,6 +21,7 @@ export const mutatorBlendColorPaintMask = ((writer: PixelWriter<any>, deps: Part
19
21
  const {
20
22
  blendColorPixelDataBinaryMask = defaults.blendColorPixelDataBinaryMask,
21
23
  blendColorPixelDataAlphaMask = defaults.blendColorPixelDataAlphaMask,
24
+ blendColorPixelData = defaults.blendColorPixelData,
22
25
  } = deps
23
26
 
24
27
  const OPTS = {
@@ -26,12 +29,14 @@ export const mutatorBlendColorPaintMask = ((writer: PixelWriter<any>, deps: Part
26
29
  y: 0,
27
30
  blendFn: sourceOverPerfect,
28
31
  alpha: 255,
32
+ w: undefined as number | undefined,
33
+ h: undefined as number | undefined,
29
34
  }
30
35
 
31
36
  return {
32
37
  blendColorPaintMask(
33
38
  color: Color32,
34
- mask: PaintMask,
39
+ mask: PaintMask | PaintRect,
35
40
  x: number,
36
41
  y: number,
37
42
  alpha = 255,
@@ -41,21 +46,32 @@ export const mutatorBlendColorPaintMask = ((writer: PixelWriter<any>, deps: Part
41
46
  const ty = y + mask.centerOffsetY
42
47
 
43
48
  const didChange = writer.accumulator.storeRegionBeforeState(tx, ty, mask.w, mask.h)
49
+ if (!didChange) return false
44
50
 
45
51
  OPTS.x = tx
46
52
  OPTS.y = ty
47
53
  OPTS.alpha = alpha
48
54
  OPTS.blendFn = blendFn
55
+ OPTS.w = undefined
56
+ OPTS.h = undefined
49
57
 
50
- if (mask.type === MaskType.BINARY) {
51
- return didChange(
52
- blendColorPixelDataBinaryMask(writer.config.target, color, mask, OPTS),
53
- )
54
- } else {
58
+ if (mask.data) {
59
+ if (mask.type === MaskType.BINARY) {
60
+ return didChange(
61
+ blendColorPixelDataBinaryMask(writer.config.target, color, mask, OPTS),
62
+ )
63
+ }
55
64
  return didChange(
56
65
  blendColorPixelDataAlphaMask(writer.config.target, color, mask, OPTS),
57
66
  )
58
67
  }
68
+
69
+ OPTS.w = mask.w
70
+ OPTS.h = mask.h
71
+
72
+ return didChange(
73
+ blendColorPixelData(writer.config.target, color, OPTS),
74
+ )
59
75
  },
60
76
  }
61
77
  }) satisfies HistoryMutator<any, Deps>
@@ -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 { _macro_paintRectCenterOffset } from '../../Internal/macros'
4
5
  import { blendColorPixelData } from '../../PixelData/blendColorPixelData'
5
6
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
@@ -43,6 +44,8 @@ export const mutatorBlendColorPaintRect = ((writer: PixelWriter<any>, deps: Deps
43
44
  OPTS.alpha = alpha
44
45
 
45
46
  const didChange = writer.accumulator.storeRegionBeforeState(topLeftX, topLeftY, brushWidth, brushHeight)
47
+ if (!didChange) return false
48
+
46
49
  return didChange(
47
50
  blendColorPixelData(
48
51
  target,
@@ -29,6 +29,7 @@ export const mutatorBlendMask = ((writer: PixelWriter<any>, deps: Partial<Deps>
29
29
  const h = opts?.h ?? src.h
30
30
 
31
31
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
32
+ if (!didChange) return false
32
33
 
33
34
  if (mask.type === MaskType.BINARY) {
34
35
  return didChange(
@@ -1,4 +1,5 @@
1
- import type { BlendColor32, Color32 } from '../../_types'
1
+ import type { BlendColor32 } from '../../_types'
2
+ import type { Color32 } from '../../Color/_color-types'
2
3
  import { blendPixel } from '../../PixelData/blendPixel'
3
4
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
4
5
 
@@ -23,6 +24,7 @@ export const mutatorBlendPixel = ((writer: PixelWriter<any>, deps: Partial<Deps>
23
24
  ): boolean {
24
25
 
25
26
  const didChange = writer.accumulator.storePixelBeforeState(x, y)
27
+ if (!didChange) return false
26
28
 
27
29
  return didChange(
28
30
  blendPixel(writer.config.target, x, y, color, alpha, blendFn),
@@ -25,6 +25,7 @@ export const mutatorBlendPixelData = ((writer: PixelWriter<any>, deps: Partial<D
25
25
  const h = opts?.h ?? src.h
26
26
 
27
27
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
28
+ if (!didChange) return false
28
29
 
29
30
  return didChange(
30
31
  blendPixelData(writer.config.target, src, opts),
@@ -1,4 +1,4 @@
1
- import type { Color32 } from '../../_types'
1
+ import type { Color32 } from '../../Color/_color-types'
2
2
  import { fillPixelData } from '../../PixelData/fillPixelData'
3
3
  import type { Rect } from '../../Rect/_rect-types'
4
4
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
@@ -18,7 +18,7 @@ export const mutatorClear = ((writer: PixelWriter<any>, deps: Deps = defaults) =
18
18
  return {
19
19
  clear(
20
20
  rect?: Partial<Rect>,
21
- ) {
21
+ ): boolean {
22
22
  const target = writer.config.target
23
23
  const x = rect?.x ?? 0
24
24
  const y = rect?.y ?? 0
@@ -26,6 +26,7 @@ export const mutatorClear = ((writer: PixelWriter<any>, deps: Deps = defaults) =
26
26
  const h = rect?.h ?? target.h
27
27
 
28
28
  const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
29
+ if (!didChange) return false
29
30
  return didChange(
30
31
  fillPixelData(target, 0 as Color32, x, y, w, h),
31
32
  )
@@ -1,4 +1,4 @@
1
- import type { Color32 } from '../../_types'
1
+ import type { Color32 } from '../../Color/_color-types'
2
2
  import { fillPixelData } from '../../PixelData/fillPixelData'
3
3
  import type { Rect } from '../../Rect/_rect-types'
4
4
  import { type HistoryMutator, PixelWriter } from '../PixelWriter'
@@ -14,44 +14,60 @@ export const mutatorFill = ((writer: PixelWriter<any>, deps: Deps = defaults) =>
14
14
  fillPixelData = defaults.fillPixelData,
15
15
  } = deps
16
16
 
17
- return {
18
- fill(
19
- color: Color32,
20
- x = 0,
21
- y = 0,
22
- w = writer.config.target.w,
23
- h = writer.config.target.h,
24
- ) {
25
- const target = writer.config.target
26
-
27
- const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
28
- return didChange(
29
- fillPixelData(target, color, x, y, w, h),
30
- )
31
- },
32
- }
33
- }) satisfies HistoryMutator<any, Deps>
17
+ const config = writer.config
34
18
 
35
- /**
36
- * @param deps - @hidden
37
- */
38
- export const mutatorFillRect = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
39
- const {
40
- fillPixelData = defaults.fillPixelData,
41
- } = deps
19
+ function fill(
20
+ color: Color32,
21
+ rect?: Partial<Rect>,
22
+ ): boolean
23
+
24
+ function fill(
25
+ color: Color32,
26
+ x: number,
27
+ y: number,
28
+ w: number,
29
+ h: number,
30
+ ): boolean
31
+ function fill(
32
+ color: Color32,
33
+ _x?: Partial<Rect> | number,
34
+ _y?: number,
35
+ _w?: number,
36
+ _h?: number,
37
+ ): boolean {
38
+ const target = config.target
39
+
40
+ const dstW = target.w
41
+ const dstH = target.h
42
+
43
+ let x: number
44
+ let y: number
45
+ let w: number
46
+ let h: number
42
47
 
43
- return {
44
- fillRect(
45
- color: Color32,
46
- rect: Rect,
47
- ) {
48
- const target = writer.config.target
49
-
50
- const didChange = writer.accumulator.storeRegionBeforeState(rect.x, rect.y, rect.w, rect.h)
51
- return didChange(
52
- fillPixelData(target, color, rect.x, rect.y, rect.w, rect.h),
53
- )
54
- },
48
+ if (typeof _x === 'number') {
49
+ x = _x
50
+ y = _y!
51
+ w = _w!
52
+ h = _h!
53
+ } else if (typeof _x === 'object') {
54
+ x = _x.x ?? 0
55
+ y = _x.y ?? 0
56
+ w = _x.w ?? dstW
57
+ h = _x.h ?? dstH
58
+ } else {
59
+ x = 0
60
+ y = 0
61
+ w = dstW
62
+ h = dstH
63
+ }
64
+
65
+ const didChange = writer.accumulator.storeRegionBeforeState(x, y, w, h)
66
+ if (!didChange) return false
67
+ return didChange(
68
+ fillPixelData(target, color, x, y, w, h),
69
+ )
55
70
  }
56
- }) satisfies HistoryMutator<any, Deps>
57
71
 
72
+ return { fill }
73
+ }) satisfies HistoryMutator<any, Deps>