pixel-data-js 0.24.0 → 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 (71) hide show
  1. package/dist/index.dev.cjs +1431 -1845
  2. package/dist/index.dev.cjs.map +1 -1
  3. package/dist/index.dev.js +1297 -1702
  4. package/dist/index.dev.js.map +1 -1
  5. package/dist/index.prod.cjs +1305 -1719
  6. package/dist/index.prod.cjs.map +1 -1
  7. package/dist/index.prod.d.ts +220 -328
  8. package/dist/index.prod.js +1423 -1828
  9. package/dist/index.prod.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/Algorithm/floodFillSelection.ts +2 -2
  12. package/src/Canvas/canvas-blend-modes.ts +28 -0
  13. package/src/History/PixelAccumulator.ts +52 -29
  14. package/src/History/PixelEngineConfig.ts +7 -9
  15. package/src/History/PixelMutator/mutatorBlendPixelData.ts +2 -2
  16. package/src/History/PixelMutator/mutatorBlendPixelDataAlphaMask.ts +2 -2
  17. package/src/History/PixelMutator/mutatorBlendPixelDataBinaryMask.ts +2 -2
  18. package/src/History/PixelMutator.ts +0 -20
  19. package/src/History/PixelPatchTiles.ts +2 -2
  20. package/src/History/PixelWriter.ts +132 -9
  21. package/src/Internal/helpers.ts +2 -0
  22. package/src/Paint/PaintBuffer.ts +269 -0
  23. package/src/{PixelTile/PaintBufferRenderer.ts → Paint/PaintBufferCanvasRenderer.ts} +13 -5
  24. package/src/Paint/makeCirclePaintAlphaMask.ts +41 -0
  25. package/src/{Mask/CircleBinaryMask.ts → Paint/makeCirclePaintBinaryMask.ts} +5 -6
  26. package/src/Paint/makePaintMask.ts +28 -0
  27. package/src/Paint/makeRectFalloffPaintAlphaMask.ts +47 -0
  28. package/src/PixelData/PixelBuffer32.ts +2 -2
  29. package/src/PixelData/PixelData.ts +1 -1
  30. package/src/PixelData/applyAlphaMaskToPixelData.ts +2 -2
  31. package/src/PixelData/applyBinaryMaskToPixelData.ts +2 -2
  32. package/src/PixelData/blendColorPixelData.ts +2 -2
  33. package/src/PixelData/blendColorPixelDataAlphaMask.ts +3 -3
  34. package/src/PixelData/blendColorPixelDataBinaryMask.ts +3 -3
  35. package/src/PixelData/blendPixel.ts +2 -2
  36. package/src/PixelData/blendPixelData.ts +3 -3
  37. package/src/PixelData/blendPixelDataAlphaMask.ts +3 -3
  38. package/src/PixelData/blendPixelDataBinaryMask.ts +3 -3
  39. package/src/PixelData/blendPixelDataPaintBuffer.ts +3 -3
  40. package/src/PixelData/clearPixelData.ts +2 -2
  41. package/src/PixelData/extractPixelData.ts +4 -4
  42. package/src/PixelData/extractPixelDataBuffer.ts +4 -4
  43. package/src/PixelData/fillPixelData.ts +5 -5
  44. package/src/PixelData/fillPixelDataBinaryMask.ts +3 -3
  45. package/src/PixelData/fillPixelDataFast.ts +5 -5
  46. package/src/PixelData/invertPixelData.ts +2 -2
  47. package/src/PixelData/pixelDataToAlphaMask.ts +2 -2
  48. package/src/PixelData/reflectPixelData.ts +3 -3
  49. package/src/PixelData/resamplePixelData.ts +2 -2
  50. package/src/PixelData/writePaintBufferToPixelData.ts +26 -0
  51. package/src/PixelData/writePixelDataBuffer.ts +5 -5
  52. package/src/Rect/trimMaskRectBounds.ts +121 -0
  53. package/src/Rect/trimRectBounds.ts +25 -116
  54. package/src/_types.ts +16 -15
  55. package/src/index.ts +9 -24
  56. package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +0 -182
  57. package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +0 -59
  58. package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +0 -172
  59. package/src/History/PixelMutator/mutatorApplyRectBrush.ts +0 -64
  60. package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +0 -184
  61. package/src/History/PixelMutator/mutatorApplyRectPencil.ts +0 -65
  62. package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +0 -166
  63. package/src/History/PixelMutator/mutatorBlendColorCircleMask.ts +0 -71
  64. package/src/Mask/CircleAlphaMask.ts +0 -32
  65. package/src/PixelData/applyRectBrushToPixelData.ts +0 -98
  66. package/src/PixelData/blendColorPixelDataCircleMask.ts +0 -92
  67. package/src/PixelTile/PaintBuffer.ts +0 -122
  68. package/src/Rect/getCircleBrushOrPencilBounds.ts +0 -43
  69. package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +0 -24
  70. package/src/Rect/getRectBrushOrPencilBounds.ts +0 -38
  71. package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +0 -26
@@ -1,182 +0,0 @@
1
- import {
2
- type AlphaMask,
3
- type BlendColor32,
4
- type CircleAlphaMask,
5
- type Color32,
6
- type ColorBlendMaskOptions,
7
- type HistoryMutator,
8
- MaskType,
9
- type Rect,
10
- } from '../../_types'
11
- import { forEachLinePoint } from '../../Algorithm/forEachLinePoint'
12
- import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
13
- import { blendColorPixelDataAlphaMask } from '../../PixelData/blendColorPixelDataAlphaMask'
14
- import { getCircleBrushOrPencilBounds } from '../../Rect/getCircleBrushOrPencilBounds'
15
- import { getCircleBrushOrPencilStrokeBounds } from '../../Rect/getCircleBrushOrPencilStrokeBounds'
16
- import { PixelWriter } from '../PixelWriter'
17
-
18
- const defaults = {
19
- forEachLinePoint,
20
- blendColorPixelDataAlphaMask,
21
- getCircleBrushOrPencilBounds,
22
- getCircleBrushOrPencilStrokeBounds,
23
- }
24
-
25
- type Deps = Partial<typeof defaults>
26
-
27
- /**
28
- * @param deps - @hidden
29
- */
30
- export const mutatorApplyCircleBrushStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
31
- const {
32
- forEachLinePoint = defaults.forEachLinePoint,
33
- blendColorPixelDataAlphaMask = defaults.blendColorPixelDataAlphaMask,
34
- getCircleBrushOrPencilBounds = defaults.getCircleBrushOrPencilBounds,
35
- getCircleBrushOrPencilStrokeBounds = defaults.getCircleBrushOrPencilStrokeBounds,
36
- } = deps
37
-
38
- const strokeBoundsOut: Rect = {
39
- x: 0,
40
- y: 0,
41
- w: 0,
42
- h: 0,
43
- }
44
-
45
- const circleBrushBounds: Rect = {
46
- x: 0,
47
- y: 0,
48
- w: 0,
49
- h: 0,
50
- }
51
-
52
- const blendColorPixelOptions: ColorBlendMaskOptions = {
53
- alpha: 255,
54
- blendFn: sourceOverPerfect,
55
- x: 0,
56
- y: 0,
57
- w: 0,
58
- h: 0,
59
- }
60
-
61
- const mask = {
62
- type: MaskType.ALPHA,
63
- data: null as unknown as Uint8Array,
64
- w: 0,
65
- h: 0,
66
- }
67
-
68
- return {
69
- applyCircleBrushStroke(
70
- color: Color32,
71
- x0: number,
72
- y0: number,
73
- x1: number,
74
- y1: number,
75
- brush: CircleAlphaMask,
76
- alpha = 255,
77
- blendFn: BlendColor32 = sourceOverPerfect,
78
- ) {
79
- const brushSize = brush.size
80
-
81
- const {
82
- x: bx,
83
- y: by,
84
- w: bw,
85
- h: bh,
86
- } = getCircleBrushOrPencilStrokeBounds(
87
- x0,
88
- y0,
89
- x1,
90
- y1,
91
- brushSize,
92
- strokeBoundsOut,
93
- )
94
-
95
- if (bw <= 0 || bh <= 0) return
96
-
97
- mask.data = new Uint8Array(bw * bh)
98
- mask.w = bw
99
- mask.h = bh
100
-
101
- const maskData = mask.data
102
- const brushData = brush.data
103
- const minOffset = brush.minOffset
104
-
105
- const target = writer.config.target
106
- const targetWidth = target.width
107
- const targetHeight = target.height
108
-
109
- forEachLinePoint(
110
- x0,
111
- y0,
112
- x1,
113
- y1,
114
- (px, py) => {
115
- const {
116
- x: cbx,
117
- y: cby,
118
- w: cbw,
119
- h: cbh,
120
- } = getCircleBrushOrPencilBounds(
121
- px,
122
- py,
123
- brushSize,
124
- targetWidth,
125
- targetHeight,
126
- circleBrushBounds,
127
- )
128
-
129
- writer.accumulator.storeRegionBeforeState(
130
- cbx,
131
- cby,
132
- cbw,
133
- cbh,
134
- )
135
-
136
- const startX = Math.max(bx, cbx)
137
- const startY = Math.max(by, cby)
138
- const endX = Math.min(bx + bw, cbx + cbw)
139
- const endY = Math.min(by + bh, cby + cbh)
140
-
141
- const unclippedStartX = Math.floor(px + minOffset)
142
- const unclippedStartY = Math.floor(py + minOffset)
143
-
144
- for (let my = startY; my < endY; my++) {
145
- const strokeMaskY = my - by
146
- const strokeMaskRowOffset = strokeMaskY * bw
147
-
148
- const brushY = my - unclippedStartY
149
- const brushRowOffset = brushY * brushSize
150
-
151
- for (let mx = startX; mx < endX; mx++) {
152
- const brushX = mx - unclippedStartX
153
- const brushVal = brushData[brushRowOffset + brushX]
154
-
155
- if (brushVal > 0) {
156
- const strokeMaskIdx = strokeMaskRowOffset + (mx - bx)
157
-
158
- if (brushVal > maskData[strokeMaskIdx]) {
159
- maskData[strokeMaskIdx] = brushVal
160
- }
161
- }
162
- }
163
- }
164
- },
165
- )
166
-
167
- blendColorPixelOptions.blendFn = blendFn
168
- blendColorPixelOptions.alpha = alpha
169
- blendColorPixelOptions.x = bx
170
- blendColorPixelOptions.y = by
171
- blendColorPixelOptions.w = bw
172
- blendColorPixelOptions.h = bh
173
-
174
- blendColorPixelDataAlphaMask(
175
- target,
176
- color,
177
- mask as AlphaMask,
178
- blendColorPixelOptions,
179
- )
180
- },
181
- }
182
- }) satisfies HistoryMutator<any, Deps>
@@ -1,59 +0,0 @@
1
- import type { BlendColor32, CircleMask, Color32, HistoryMutator, Rect } from '../../_types'
2
- import { blendColorPixelDataCircleMask } from '../../PixelData/blendColorPixelDataCircleMask'
3
- import { getCircleBrushOrPencilBounds } from '../../Rect/getCircleBrushOrPencilBounds'
4
- import { PixelWriter } from '../PixelWriter'
5
-
6
- const defaults = {
7
- applyCircleMaskToPixelData: blendColorPixelDataCircleMask,
8
- getCircleBrushOrPencilBounds,
9
- }
10
-
11
- type Deps = Partial<typeof defaults>
12
-
13
- /**
14
- * @param deps - @hidden
15
- **/
16
- export const mutatorApplyCirclePencil = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
17
- const {
18
- applyCircleMaskToPixelData = defaults.applyCircleMaskToPixelData,
19
- getCircleBrushOrPencilBounds = defaults.getCircleBrushOrPencilBounds,
20
- } = deps
21
-
22
- const boundsOut: Rect = { x: 0, y: 0, w: 0, h: 0 }
23
-
24
- return {
25
- applyCirclePencil(
26
- color: Color32,
27
- centerX: number,
28
- centerY: number,
29
- brush: CircleMask,
30
- alpha = 255,
31
- blendFn?: BlendColor32,
32
- ): boolean {
33
-
34
- const target = writer.config.target
35
- const b = getCircleBrushOrPencilBounds(
36
- centerX,
37
- centerY,
38
- brush.size,
39
- target.width,
40
- target.height,
41
- boundsOut,
42
- )
43
-
44
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h)
45
- return didChange(
46
- applyCircleMaskToPixelData(
47
- target,
48
- color,
49
- centerX,
50
- centerY,
51
- brush,
52
- alpha,
53
- blendFn,
54
- b,
55
- ),
56
- )
57
- },
58
- }
59
- }) satisfies HistoryMutator<any, Deps>
@@ -1,172 +0,0 @@
1
- import {
2
- type BinaryMask,
3
- type BlendColor32,
4
- type CircleBinaryMask,
5
- type Color32,
6
- type ColorBlendMaskOptions,
7
- type HistoryMutator,
8
- MaskType,
9
- type Rect,
10
- } from '../../_types'
11
- import { forEachLinePoint } from '../../Algorithm/forEachLinePoint'
12
- import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
13
- import { blendColorPixelDataBinaryMask } from '../../PixelData/blendColorPixelDataBinaryMask'
14
- import { getCircleBrushOrPencilBounds } from '../../Rect/getCircleBrushOrPencilBounds'
15
- import { getCircleBrushOrPencilStrokeBounds } from '../../Rect/getCircleBrushOrPencilStrokeBounds'
16
- import { PixelWriter } from '../PixelWriter'
17
-
18
- const defaults = {
19
- forEachLinePoint,
20
- blendColorPixelDataBinaryMask,
21
- getCircleBrushOrPencilBounds,
22
- getCircleBrushOrPencilStrokeBounds,
23
- }
24
-
25
- type Deps = Partial<typeof defaults>
26
-
27
- /**
28
- * @param deps - @hidden
29
- */
30
- export const mutatorApplyCirclePencilStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
31
- const {
32
- forEachLinePoint = defaults.forEachLinePoint,
33
- blendColorPixelDataBinaryMask = defaults.blendColorPixelDataBinaryMask,
34
- getCircleBrushOrPencilStrokeBounds = defaults.getCircleBrushOrPencilStrokeBounds,
35
- getCircleBrushOrPencilBounds = defaults.getCircleBrushOrPencilBounds,
36
- } = deps
37
-
38
- const strokeBoundsOut: Rect = {
39
- x: 0,
40
- y: 0,
41
- w: 0,
42
- h: 0,
43
- }
44
-
45
- const circlePencilBounds: Rect = {
46
- x: 0,
47
- y: 0,
48
- w: 0,
49
- h: 0,
50
- }
51
-
52
- const blendColorPixelOptions: ColorBlendMaskOptions = {
53
- alpha: 255,
54
- blendFn: sourceOverPerfect,
55
- x: 0,
56
- y: 0,
57
- w: 0,
58
- h: 0,
59
- }
60
-
61
- const mask = {
62
- type: MaskType.BINARY,
63
- data: null as unknown as Uint8Array,
64
- w: 0,
65
- h: 0,
66
- }
67
-
68
- return {
69
- applyCirclePencilStroke(
70
- color: Color32,
71
- x0: number,
72
- y0: number,
73
- x1: number,
74
- y1: number,
75
- brush: CircleBinaryMask,
76
- alpha = 255,
77
- blendFn: BlendColor32 = sourceOverPerfect,
78
- ) {
79
- const {
80
- x: bx,
81
- y: by,
82
- w: bw,
83
- h: bh,
84
- } = getCircleBrushOrPencilStrokeBounds(
85
- x0,
86
- y0,
87
- x1,
88
- y1,
89
- brush.size,
90
- strokeBoundsOut,
91
- )
92
-
93
- if (bw <= 0 || bh <= 0) return
94
-
95
- mask.data = new Uint8Array(bw * bh)
96
- mask.w = bw
97
- mask.h = bh
98
-
99
- const maskData = mask.data
100
- const target = writer.config.target
101
- const targetWidth = target.width
102
- const targetHeight = target.height
103
-
104
- forEachLinePoint(
105
- x0,
106
- y0,
107
- x1,
108
- y1,
109
- (px, py) => {
110
- const {
111
- x: cbx,
112
- y: cby,
113
- w: cbw,
114
- h: cbh,
115
- } = getCircleBrushOrPencilBounds(
116
- px,
117
- py,
118
- brush.size,
119
- targetWidth,
120
- targetHeight,
121
- circlePencilBounds,
122
- )
123
-
124
- writer.accumulator.storeRegionBeforeState(
125
- cbx,
126
- cby,
127
- cbw,
128
- cbh,
129
- )
130
-
131
- const unclippedStartX = Math.floor(px + brush.minOffset)
132
- const unclippedStartY = Math.floor(py + brush.minOffset)
133
-
134
- const startX = Math.max(bx, unclippedStartX)
135
- const startY = Math.max(by, unclippedStartY)
136
- const endX = Math.min(bx + bw, unclippedStartX + brush.w)
137
- const endY = Math.min(by + bh, unclippedStartY + brush.h)
138
-
139
- for (let my = startY; my < endY; my++) {
140
- const brushY = my - unclippedStartY
141
- const maskRowOffset = (my - by) * bw
142
- const brushRowOffset = brushY * brush.w
143
-
144
- for (let mx = startX; mx < endX; mx++) {
145
- const brushX = mx - unclippedStartX
146
- const brushAlpha = brush.data[brushRowOffset + brushX]
147
-
148
- if (brushAlpha > 0) {
149
- const maskIdx = maskRowOffset + (mx - bx)
150
- maskData[maskIdx] = brushAlpha
151
- }
152
- }
153
- }
154
- },
155
- )
156
-
157
- blendColorPixelOptions.blendFn = blendFn
158
- blendColorPixelOptions.alpha = alpha
159
- blendColorPixelOptions.x = bx
160
- blendColorPixelOptions.y = by
161
- blendColorPixelOptions.w = bw
162
- blendColorPixelOptions.h = bh
163
-
164
- blendColorPixelDataBinaryMask(
165
- target,
166
- color,
167
- mask as BinaryMask,
168
- blendColorPixelOptions,
169
- )
170
- },
171
- }
172
- }) satisfies HistoryMutator<any, Deps>
@@ -1,64 +0,0 @@
1
- import type { BlendColor32, Color32, HistoryMutator, Rect } from '../../_types'
2
- import { applyRectBrushToPixelData } from '../../PixelData/applyRectBrushToPixelData'
3
- import { getRectBrushOrPencilBounds } from '../../Rect/getRectBrushOrPencilBounds'
4
- import { PixelWriter } from '../PixelWriter'
5
-
6
- const defaults = {
7
- applyRectBrushToPixelData,
8
- getRectBrushOrPencilBounds,
9
- }
10
-
11
- type Deps = Partial<typeof defaults>
12
-
13
- /**
14
- * @param deps - @hidden
15
- */
16
- export const mutatorApplyRectBrush = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
17
- const {
18
- applyRectBrushToPixelData = defaults.applyRectBrushToPixelData,
19
- getRectBrushOrPencilBounds = defaults.getRectBrushOrPencilBounds,
20
- } = deps
21
-
22
- const boundsOut: Rect = { x: 0, y: 0, w: 0, h: 0 }
23
-
24
- return {
25
- applyRectBrush(
26
- color: Color32,
27
- centerX: number,
28
- centerY: number,
29
- brushWidth: number,
30
- brushHeight: number,
31
- alpha = 255,
32
- fallOff: (dist: number) => number,
33
- blendFn?: BlendColor32,
34
- ): boolean {
35
-
36
- const target = writer.config.target
37
- const b = getRectBrushOrPencilBounds(
38
- centerX,
39
- centerY,
40
- brushWidth,
41
- brushHeight,
42
- target.width,
43
- target.height,
44
- boundsOut,
45
- )
46
-
47
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h)
48
- return didChange(
49
- applyRectBrushToPixelData(
50
- target,
51
- color,
52
- centerX,
53
- centerY,
54
- brushWidth,
55
- brushHeight,
56
- alpha,
57
- fallOff,
58
- blendFn,
59
- b,
60
- ),
61
- )
62
- },
63
- }
64
- }) satisfies HistoryMutator<any, Deps>
@@ -1,184 +0,0 @@
1
- import {
2
- type AlphaMask,
3
- type BlendColor32,
4
- type Color32,
5
- type ColorBlendOptions,
6
- type HistoryMutator,
7
- MaskType,
8
- type Rect,
9
- } from '../../_types'
10
- import { forEachLinePoint } from '../../Algorithm/forEachLinePoint'
11
- import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
12
- import { blendColorPixelDataAlphaMask } from '../../PixelData/blendColorPixelDataAlphaMask'
13
- import { getRectBrushOrPencilBounds } from '../../Rect/getRectBrushOrPencilBounds'
14
- import { getRectBrushOrPencilStrokeBounds } from '../../Rect/getRectBrushOrPencilStrokeBounds'
15
- import { PixelWriter } from '../PixelWriter'
16
-
17
- const defaults = {
18
- forEachLinePoint,
19
- blendColorPixelDataAlphaMask,
20
- getRectBrushOrPencilBounds,
21
- getRectBrushOrPencilStrokeBounds,
22
- }
23
-
24
- type Deps = Partial<typeof defaults>
25
-
26
- /**
27
- * @param deps - @hidden
28
- */
29
- export const mutatorApplyRectBrushStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
30
- const {
31
- forEachLinePoint = defaults.forEachLinePoint,
32
- blendColorPixelDataAlphaMask = defaults.blendColorPixelDataAlphaMask,
33
- getRectBrushOrPencilBounds = defaults.getRectBrushOrPencilBounds,
34
- getRectBrushOrPencilStrokeBounds = defaults.getRectBrushOrPencilStrokeBounds,
35
- } = deps
36
-
37
- const strokeBoundsOut: Rect = {
38
- x: 0,
39
- y: 0,
40
- w: 0,
41
- h: 0,
42
- }
43
-
44
- const rectBrushBounds: Rect = {
45
- x: 0,
46
- y: 0,
47
- w: 0,
48
- h: 0,
49
- }
50
-
51
- const blendColorPixelOptions: ColorBlendOptions = {
52
- alpha: 255,
53
- blendFn: sourceOverPerfect,
54
- x: 0,
55
- y: 0,
56
- w: 0,
57
- h: 0,
58
- }
59
-
60
- const mask = {
61
- type: MaskType.ALPHA,
62
- data: null as unknown as Uint8Array,
63
- w: 0,
64
- h: 0,
65
- }
66
-
67
- return {
68
- applyRectBrushStroke(
69
- color: Color32,
70
- x0: number,
71
- y0: number,
72
- x1: number,
73
- y1: number,
74
- brushWidth: number,
75
- brushHeight: number,
76
- alpha = 255,
77
- fallOff: (dist: number) => number,
78
- blendFn: BlendColor32 = sourceOverPerfect,
79
- ) {
80
- const {
81
- x: bx,
82
- y: by,
83
- w: bw,
84
- h: bh,
85
- } = getRectBrushOrPencilStrokeBounds(
86
- x0,
87
- y0,
88
- x1,
89
- y1,
90
- brushWidth,
91
- brushHeight,
92
- strokeBoundsOut,
93
- )
94
-
95
- if (bw <= 0 || bh <= 0) return
96
-
97
- mask.data = new Uint8Array(bw * bh)
98
- mask.w = bw
99
- mask.h = bh
100
-
101
- const maskData = mask.data
102
- const halfW = brushWidth / 2
103
- const halfH = brushHeight / 2
104
- const invHalfW = 1 / halfW
105
- const invHalfH = 1 / halfH
106
-
107
- // Restore the pixel-art centering logic
108
- const centerOffsetX = (brushWidth % 2 === 0) ? 0.5 : 0
109
- const centerOffsetY = (brushHeight % 2 === 0) ? 0.5 : 0
110
-
111
- const target = writer.config.target
112
- const targetWidth = target.width
113
- const targetHeight = target.height
114
-
115
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
116
- const {
117
- x: rbx,
118
- y: rby,
119
- w: rbw,
120
- h: rbh,
121
- } = getRectBrushOrPencilBounds(
122
- px,
123
- py,
124
- brushWidth,
125
- brushHeight,
126
- targetWidth,
127
- targetHeight,
128
- rectBrushBounds,
129
- )
130
-
131
- writer.accumulator.storeRegionBeforeState(
132
- rbx,
133
- rby,
134
- rbw,
135
- rbh,
136
- )
137
-
138
- const startX = Math.max(bx, rbx)
139
- const startY = Math.max(by, rby)
140
- const endX = Math.min(bx + bw, rbx + rbw)
141
- const endY = Math.min(by + bh, rby + rbh)
142
-
143
- // Snapped origin for this specific point on the line
144
- const fPx = Math.floor(px)
145
- const fPy = Math.floor(py)
146
-
147
- for (let my = startY; my < endY; my++) {
148
- const dy = Math.abs((my - fPy) + centerOffsetY) * invHalfH
149
- const maskRowOffset = (my - by) * bw
150
-
151
- for (let mx = startX; mx < endX; mx++) {
152
- const dx = Math.abs((mx - fPx) + centerOffsetX) * invHalfW
153
- const maskIdx = maskRowOffset + (mx - bx)
154
-
155
- const dist = dx > dy ? dx : dy
156
- const strength = fallOff!(dist)
157
-
158
- if (strength > 0) {
159
- const intensity = (strength * 255) | 0
160
-
161
- if (intensity > maskData[maskIdx]) {
162
- maskData[maskIdx] = intensity
163
- }
164
- }
165
- }
166
- }
167
- })
168
-
169
- blendColorPixelOptions.blendFn = blendFn
170
- blendColorPixelOptions.alpha = alpha
171
- blendColorPixelOptions.x = bx
172
- blendColorPixelOptions.y = by
173
- blendColorPixelOptions.w = bw
174
- blendColorPixelOptions.h = bh
175
-
176
- blendColorPixelDataAlphaMask(
177
- target,
178
- color,
179
- mask as AlphaMask,
180
- blendColorPixelOptions,
181
- )
182
- },
183
- }
184
- }) satisfies HistoryMutator<any, Deps>