pixel-data-js 0.23.1 → 0.25.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 (85) hide show
  1. package/dist/index.dev.cjs +1816 -1802
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1799 -1786
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1816 -1802
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +349 -342
  8. package/dist/index.prod.js +1799 -1786
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +3 -2
  11. package/src/Algorithm/floodFillSelection.ts +2 -2
  12. package/src/Canvas/CanvasFrameRenderer.ts +57 -0
  13. package/src/Canvas/ReusableCanvas.ts +60 -11
  14. package/src/Canvas/canvas-blend-modes.ts +28 -0
  15. package/src/History/HistoryAction.ts +38 -0
  16. package/src/History/HistoryManager.ts +4 -8
  17. package/src/History/PixelAccumulator.ts +137 -99
  18. package/src/History/PixelEngineConfig.ts +16 -6
  19. package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +6 -6
  20. package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +6 -6
  21. package/src/History/PixelMutator/mutatorBlendColor.ts +8 -5
  22. package/src/History/PixelMutator/mutatorBlendPixel.ts +22 -26
  23. package/src/History/PixelMutator/mutatorBlendPixelData.ts +7 -5
  24. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +7 -5
  25. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +7 -5
  26. package/src/History/PixelMutator/mutatorClear.ts +6 -5
  27. package/src/History/PixelMutator/mutatorFill.ts +34 -9
  28. package/src/History/PixelMutator/mutatorFillBinaryMask.ts +4 -2
  29. package/src/History/PixelMutator/mutatorInvert.ts +8 -4
  30. package/src/History/PixelMutator.ts +2 -21
  31. package/src/History/PixelPatchTiles.ts +4 -16
  32. package/src/History/PixelWriter.ts +150 -31
  33. package/src/ImageData/ReusableImageData.ts +3 -5
  34. package/src/Internal/helpers.ts +2 -0
  35. package/src/Paint/PaintBuffer.ts +269 -0
  36. package/src/Paint/PaintBufferCanvasRenderer.ts +48 -0
  37. package/src/Paint/makeCirclePaintAlphaMask.ts +41 -0
  38. package/src/{Mask/CircleBrushBinaryMask.ts → Paint/makeCirclePaintBinaryMask.ts} +5 -6
  39. package/src/Paint/makePaintMask.ts +28 -0
  40. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +47 -0
  41. package/src/PixelData/PixelBuffer32.ts +2 -2
  42. package/src/PixelData/PixelData.ts +2 -28
  43. package/src/PixelData/applyAlphaMaskToPixelData.ts +20 -10
  44. package/src/PixelData/applyBinaryMaskToPixelData.ts +26 -19
  45. package/src/PixelData/blendColorPixelData.ts +33 -9
  46. package/src/PixelData/blendColorPixelDataAlphaMask.ts +19 -9
  47. package/src/PixelData/blendColorPixelDataBinaryMask.ts +19 -10
  48. package/src/PixelData/blendPixel.ts +47 -0
  49. package/src/PixelData/blendPixelData.ts +17 -7
  50. package/src/PixelData/blendPixelDataAlphaMask.ts +15 -7
  51. package/src/PixelData/blendPixelDataBinaryMask.ts +16 -7
  52. package/src/PixelData/blendPixelDataPaintBuffer.ts +37 -0
  53. package/src/PixelData/clearPixelData.ts +4 -4
  54. package/src/PixelData/extractPixelData.ts +4 -4
  55. package/src/PixelData/extractPixelDataBuffer.ts +4 -4
  56. package/src/PixelData/fillPixelData.ts +31 -21
  57. package/src/PixelData/fillPixelDataBinaryMask.ts +15 -7
  58. package/src/PixelData/fillPixelDataFast.ts +94 -0
  59. package/src/PixelData/invertPixelData.ts +6 -4
  60. package/src/PixelData/pixelDataToAlphaMask.ts +2 -2
  61. package/src/PixelData/reflectPixelData.ts +3 -3
  62. package/src/PixelData/resamplePixelData.ts +2 -2
  63. package/src/PixelData/writePaintBufferToPixelData.ts +26 -0
  64. package/src/PixelData/writePixelDataBuffer.ts +5 -5
  65. package/src/PixelTile/PixelTile.ts +21 -0
  66. package/src/PixelTile/PixelTilePool.ts +63 -0
  67. package/src/Rect/trimMaskRectBounds.ts +121 -0
  68. package/src/Rect/trimRectBounds.ts +25 -116
  69. package/src/_types.ts +17 -16
  70. package/src/index.ts +19 -24
  71. package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +0 -78
  72. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +0 -181
  73. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +0 -59
  74. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +0 -171
  75. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +0 -64
  76. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +0 -182
  77. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +0 -65
  78. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +0 -164
  79. package/src/Mask/CircleBrushAlphaMask.ts +0 -32
  80. package/src/PixelData/applyCircleBrushToPixelData.ts +0 -91
  81. package/src/PixelData/applyRectBrushToPixelData.ts +0 -85
  82. package/src/Rect/getCircleBrushOrPencilBounds.ts +0 -43
  83. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +0 -24
  84. package/src/Rect/getRectBrushOrPencilBounds.ts +0 -38
  85. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +0 -26
@@ -1,91 +0,0 @@
1
- import {
2
- type BlendColor32,
3
- type CircleBrushMask,
4
- type Color32,
5
- type ColorBlendMaskOptions,
6
- type IPixelData,
7
- MaskType,
8
- type Rect,
9
- } from '../_types'
10
- import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
11
- import { getCircleBrushOrPencilBounds } from '../Rect/getCircleBrushOrPencilBounds'
12
- import { blendColorPixelDataAlphaMask } from './blendColorPixelDataAlphaMask'
13
- import { blendColorPixelDataBinaryMask } from './blendColorPixelDataBinaryMask'
14
-
15
- /**
16
- * Applies a circular brush to pixel data using a pre-calculated alpha mask.
17
- *
18
- * @param target The PixelData to modify.
19
- * @param color The brush color.
20
- * @param centerX The center x-coordinate of the brush.
21
- * @param centerY The center y-coordinate of the brush.
22
- * @param brush The pre-calculated CircleBrushAlphaMask.
23
- * @param alpha The overall opacity of the brush (0-255).
24
- * @param blendFn
25
- * @param scratchOptions
26
- * @param bounds precalculated result from {@link getCircleBrushOrPencilBounds}
27
- */
28
- export function applyCircleBrushToPixelData(
29
- target: IPixelData,
30
- color: Color32,
31
- centerX: number,
32
- centerY: number,
33
- brush: CircleBrushMask,
34
- alpha = 255,
35
- blendFn: BlendColor32 = sourceOverPerfect,
36
- scratchOptions: ColorBlendMaskOptions = {},
37
- bounds?: Rect,
38
- ): void {
39
- const b = bounds ?? getCircleBrushOrPencilBounds(
40
- centerX,
41
- centerY,
42
- brush.size,
43
- target.width,
44
- target.height,
45
- )
46
-
47
- if (b.w <= 0 || b.h <= 0) return
48
-
49
- const unclippedStartX = Math.floor(centerX + brush.minOffset)
50
- const unclippedStartY = Math.floor(centerY + brush.minOffset)
51
-
52
- // Calculate the intersection between the unclipped mask rect and the allowed bounds
53
- const ix = Math.max(unclippedStartX, b.x)
54
- const iy = Math.max(unclippedStartY, b.y)
55
- const ir = Math.min(unclippedStartX + brush.w, b.x + b.w)
56
- const ib = Math.min(unclippedStartY + brush.h, b.y + b.h)
57
-
58
- const iw = ir - ix
59
- const ih = ib - iy
60
-
61
- // If the mask falls entirely outside the bounds, exit
62
- if (iw <= 0 || ih <= 0) return
63
-
64
- // Apply the intersected coordinates and internal mask offsets
65
- scratchOptions.x = ix
66
- scratchOptions.y = iy
67
- scratchOptions.w = iw
68
- scratchOptions.h = ih
69
- scratchOptions.mx = ix - unclippedStartX
70
- scratchOptions.my = iy - unclippedStartY
71
- scratchOptions.alpha = alpha
72
- scratchOptions.blendFn = blendFn
73
-
74
- if (brush.type === MaskType.ALPHA) {
75
- blendColorPixelDataAlphaMask(
76
- target,
77
- color,
78
- brush,
79
- scratchOptions,
80
- )
81
- }
82
-
83
- if (brush.type === MaskType.BINARY) {
84
- blendColorPixelDataBinaryMask(
85
- target,
86
- color,
87
- brush,
88
- scratchOptions,
89
- )
90
- }
91
- }
@@ -1,85 +0,0 @@
1
- import type { BlendColor32, Color32, IPixelData, Rect } from '../_types'
2
- import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
3
- import { getRectBrushOrPencilBounds } from '../Rect/getRectBrushOrPencilBounds'
4
-
5
- export function applyRectBrushToPixelData(
6
- target: IPixelData,
7
- color: Color32,
8
- centerX: number,
9
- centerY: number,
10
- brushWidth: number,
11
- brushHeight: number,
12
- alpha = 255,
13
- fallOff: (dist: number) => number,
14
- blendFn: BlendColor32 = sourceOverPerfect,
15
- bounds?: Rect,
16
- ): void {
17
- const targetWidth = target.width
18
- const targetHeight = target.height
19
-
20
- const b = bounds ?? getRectBrushOrPencilBounds(
21
- centerX,
22
- centerY,
23
- brushWidth,
24
- brushHeight,
25
- targetWidth,
26
- targetHeight,
27
- )
28
-
29
- if (b.w <= 0 || b.h <= 0) return
30
-
31
- const data32 = target.data32
32
- const baseColor = color & 0x00ffffff
33
- const baseSrcAlpha = color >>> 24
34
- const isOpaque = alpha === 255
35
-
36
- const invHalfW = 1 / (brushWidth / 2)
37
- const invHalfH = 1 / (brushHeight / 2)
38
-
39
- // Restore the pixel-art centering logic
40
- const centerOffsetX = (brushWidth % 2 === 0) ? 0.5 : 0
41
- const centerOffsetY = (brushHeight % 2 === 0) ? 0.5 : 0
42
- const fCenterX = Math.floor(centerX)
43
- const fCenterY = Math.floor(centerY)
44
-
45
- const endX = b.x + b.w
46
- const endY = b.y + b.h
47
- const isOverwrite = (blendFn as any).isOverwrite
48
-
49
- for (let py = b.y; py < endY; py++) {
50
- const rowOffset = py * targetWidth
51
- const dy = Math.abs((py - fCenterY) + centerOffsetY) * invHalfH
52
-
53
- for (let px = b.x; px < endX; px++) {
54
- const idx = rowOffset + px
55
-
56
- const dx = Math.abs((px - fCenterX) + centerOffsetX) * invHalfW
57
- const dist = dx > dy ? dx : dy
58
-
59
- const strength = fallOff(dist)
60
- const maskVal = (strength * 255) | 0
61
-
62
- if (maskVal <= 0) continue
63
-
64
- let weight = alpha
65
-
66
- if (isOpaque) {
67
- weight = maskVal
68
- } else if (maskVal !== 255) {
69
- weight = (maskVal * alpha + 128) >> 8
70
- }
71
-
72
- let finalCol = color
73
-
74
- if (weight < 255) {
75
- const a = (baseSrcAlpha * weight + 128) >> 8
76
-
77
- if (a === 0 && !isOverwrite) continue
78
-
79
- finalCol = ((a << 24) | baseColor) >>> 0 as Color32
80
- }
81
-
82
- data32[idx] = blendFn(finalCol, data32[idx] as Color32)
83
- }
84
- }
85
- }
@@ -1,43 +0,0 @@
1
- import type { Rect } from '../_types'
2
-
3
- export function getCircleBrushOrPencilBounds(
4
- centerX: number,
5
- centerY: number,
6
- brushSize: number,
7
- targetWidth: number,
8
- targetHeight: number,
9
- out?: Rect,
10
- ): Rect {
11
- const r = brushSize / 2
12
-
13
- const minOffset = -Math.ceil(r - 0.5)
14
- const maxOffset = Math.floor(r - 0.5)
15
-
16
- // start is inclusive, end is exclusive
17
- const startX = Math.floor(centerX + minOffset)
18
- const startY = Math.floor(centerY + minOffset)
19
- const endX = Math.floor(centerX + maxOffset) + 1
20
- const endY = Math.floor(centerY + maxOffset) + 1
21
-
22
- const res = out ?? {
23
- x: 0,
24
- y: 0,
25
- w: 0,
26
- h: 0,
27
- }
28
-
29
- const cStartX = Math.max(0, startX)
30
- const cStartY = Math.max(0, startY)
31
- const cEndX = Math.min(targetWidth, endX)
32
- const cEndY = Math.min(targetHeight, endY)
33
-
34
- const w = cEndX - cStartX
35
- const h = cEndY - cStartY
36
-
37
- res.x = cStartX
38
- res.y = cStartY
39
- res.w = w < 0 ? 0 : w
40
- res.h = h < 0 ? 0 : h
41
-
42
- return res
43
- }
@@ -1,24 +0,0 @@
1
- import type { Rect } from '../_types'
2
-
3
- export function getCircleBrushOrPencilStrokeBounds(
4
- x0: number,
5
- y0: number,
6
- x1: number,
7
- y1: number,
8
- brushSize: number,
9
- result: Rect,
10
- ): Rect {
11
- const r = Math.ceil(brushSize / 2)
12
-
13
- const minX = Math.min(x0, x1) - r
14
- const minY = Math.min(y0, y1) - r
15
- const maxX = Math.max(x0, x1) + r
16
- const maxY = Math.max(x0, y1) + r
17
-
18
- result.x = Math.floor(minX)
19
- result.y = Math.floor(minY)
20
- result.w = Math.ceil(maxX - minX)
21
- result.h = Math.ceil(maxY - minY)
22
-
23
- return result
24
- }
@@ -1,38 +0,0 @@
1
- import type { Rect } from '../_types'
2
-
3
- export function getRectBrushOrPencilBounds(
4
- centerX: number,
5
- centerY: number,
6
- brushWidth: number,
7
- brushHeight: number,
8
- targetWidth: number,
9
- targetHeight: number,
10
- out?: Rect,
11
- ): Rect {
12
- const startX = Math.floor(centerX - brushWidth / 2)
13
- const startY = Math.floor(centerY - brushHeight / 2)
14
- const endX = startX + brushWidth
15
- const endY = startY + brushHeight
16
-
17
- const res = out ?? {
18
- x: 0,
19
- y: 0,
20
- w: 0,
21
- h: 0,
22
- }
23
-
24
- const cStartX = Math.max(0, startX)
25
- const cStartY = Math.max(0, startY)
26
- const cEndX = Math.min(targetWidth, endX)
27
- const cEndY = Math.min(targetHeight, endY)
28
-
29
- const w = cEndX - cStartX
30
- const h = cEndY - cStartY
31
-
32
- res.x = cStartX
33
- res.y = cStartY
34
- res.w = w < 0 ? 0 : w
35
- res.h = h < 0 ? 0 : h
36
-
37
- return res
38
- }
@@ -1,26 +0,0 @@
1
- import type { Rect } from '../_types'
2
-
3
- export function getRectBrushOrPencilStrokeBounds(
4
- x0: number,
5
- y0: number,
6
- x1: number,
7
- y1: number,
8
- brushWidth: number,
9
- brushHeight: number,
10
- result: Rect,
11
- ): Rect {
12
- const halfW = brushWidth / 2
13
- const halfH = brushHeight / 2
14
-
15
- const minX = Math.min(x0, x1) - halfW
16
- const minY = Math.min(y0, y1) - halfH
17
- const maxX = Math.max(x0, x1) + halfW
18
- const maxY = Math.max(y0, y1) + halfH
19
-
20
- result.x = Math.floor(minX)
21
- result.y = Math.floor(minY)
22
- result.w = Math.ceil(maxX - minX)
23
- result.h = Math.ceil(maxY - minY)
24
-
25
- return result
26
- }