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,65 +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
- fallOff: () => 1,
10
- }
11
-
12
- type Deps = Partial<typeof defaults>
13
-
14
- /**
15
- * @param deps - @hidden
16
- */
17
- export const mutatorApplyRectPencil = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
18
- const {
19
- applyRectBrushToPixelData = defaults.applyRectBrushToPixelData,
20
- getRectBrushOrPencilBounds = defaults.getRectBrushOrPencilBounds,
21
- fallOff = defaults.fallOff,
22
- } = deps
23
-
24
- const boundsOut: Rect = { x: 0, y: 0, w: 0, h: 0 }
25
-
26
- return {
27
- applyRectPencil(
28
- color: Color32,
29
- centerX: number,
30
- centerY: number,
31
- brushWidth: number,
32
- brushHeight: number,
33
- alpha = 255,
34
- blendFn?: BlendColor32,
35
- ): boolean {
36
-
37
- const target = writer.config.target
38
- const b = getRectBrushOrPencilBounds(
39
- centerX,
40
- centerY,
41
- brushWidth,
42
- brushHeight,
43
- target.width,
44
- target.height,
45
- boundsOut,
46
- )
47
-
48
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h)
49
- return didChange(
50
- applyRectBrushToPixelData(
51
- target,
52
- color,
53
- centerX,
54
- centerY,
55
- brushWidth,
56
- brushHeight,
57
- alpha,
58
- fallOff,
59
- blendFn,
60
- b,
61
- ),
62
- )
63
- },
64
- }
65
- }) satisfies HistoryMutator<any, Deps>
@@ -1,166 +0,0 @@
1
- import {
2
- type BinaryMask,
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 { blendColorPixelDataBinaryMask } from '../../PixelData/blendColorPixelDataBinaryMask'
13
- import { getRectBrushOrPencilBounds } from '../../Rect/getRectBrushOrPencilBounds'
14
- import { getRectBrushOrPencilStrokeBounds } from '../../Rect/getRectBrushOrPencilStrokeBounds'
15
- import { PixelWriter } from '../PixelWriter'
16
-
17
- const defaults = {
18
- forEachLinePoint,
19
- getRectBrushOrPencilBounds,
20
- getRectBrushOrPencilStrokeBounds,
21
- blendColorPixelDataBinaryMask,
22
- }
23
-
24
- type Deps = Partial<typeof defaults>
25
-
26
- /**
27
- * @param deps - @hidden
28
- */
29
- export const mutatorApplyRectPencilStroke = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
30
- const {
31
- forEachLinePoint = defaults.forEachLinePoint,
32
- blendColorPixelDataBinaryMask = defaults.blendColorPixelDataBinaryMask,
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 rectPencilBounds: 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.BINARY,
62
- data: null as unknown as Uint8Array,
63
- w: 0,
64
- h: 0,
65
- }
66
-
67
- return {
68
- applyRectPencilStroke(
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
- blendFn: BlendColor32 = sourceOverPerfect,
78
- ) {
79
- const {
80
- x: bx,
81
- y: by,
82
- w: bw,
83
- h: bh,
84
- } = getRectBrushOrPencilStrokeBounds(
85
- x0,
86
- y0,
87
- x1,
88
- y1,
89
- brushWidth,
90
- brushHeight,
91
- strokeBoundsOut,
92
- )
93
-
94
- if (bw <= 0 || bh <= 0) return
95
-
96
- mask.data = new Uint8Array(bw * bh)
97
- mask.w = bw
98
- mask.h = bh
99
-
100
- const maskData = mask.data
101
-
102
- const halfW = brushWidth / 2
103
- const halfH = brushHeight / 2
104
- const centerOffset = (brushWidth % 2 === 0) ? 0.5 : 0
105
-
106
- const target = writer.config.target
107
- const targetWidth = target.width
108
- const targetHeight = target.height
109
-
110
- forEachLinePoint(x0, y0, x1, y1, (px, py) => {
111
- const {
112
- x: rbx,
113
- y: rby,
114
- w: rbw,
115
- h: rbh,
116
- } = getRectBrushOrPencilBounds(
117
- px,
118
- py,
119
- brushWidth,
120
- brushHeight,
121
- targetWidth,
122
- targetHeight,
123
- rectPencilBounds,
124
- )
125
-
126
- writer.accumulator.storeRegionBeforeState(
127
- rbx,
128
- rby,
129
- rbw,
130
- rbh,
131
- )
132
-
133
- const startX = Math.max(bx, rbx)
134
- const startY = Math.max(by, rby)
135
- const endX = Math.min(bx + bw, rbx + rbw)
136
- const endY = Math.min(by + bh, rby + rbh)
137
-
138
- const fPx = Math.floor(px)
139
- const fPy = Math.floor(py)
140
-
141
- for (let my = startY; my < endY; my++) {
142
- const dy = Math.abs((my - fPy) + centerOffset)
143
- const maskRowOffset = (my - by) * bw
144
-
145
- for (let mx = startX; mx < endX; mx++) {
146
- const dx = Math.abs((mx - fPx) + centerOffset)
147
- const maskIdx = maskRowOffset + (mx - bx)
148
-
149
- if (dx <= halfW && dy <= halfH) {
150
- maskData[maskIdx] = 1
151
- }
152
- }
153
- }
154
- })
155
-
156
- blendColorPixelOptions.blendFn = blendFn
157
- blendColorPixelOptions.alpha = alpha
158
- blendColorPixelOptions.x = bx
159
- blendColorPixelOptions.y = by
160
- blendColorPixelOptions.w = bw
161
- blendColorPixelOptions.h = bh
162
-
163
- blendColorPixelDataBinaryMask(target, color, mask as BinaryMask, blendColorPixelOptions)
164
- },
165
- }
166
- }) satisfies HistoryMutator<any, Deps>
@@ -1,71 +0,0 @@
1
- import type { BlendColor32, CircleMask, Color32, ColorBlendMaskOptions, HistoryMutator, Rect } from '../../_types'
2
- import { sourceOverPerfect } from '../../BlendModes/blend-modes-perfect'
3
- import { blendColorPixelDataCircleMask } from '../../PixelData/blendColorPixelDataCircleMask'
4
- import { getCircleBrushOrPencilBounds } from '../../Rect/getCircleBrushOrPencilBounds'
5
- import { PixelWriter } from '../PixelWriter'
6
-
7
- const defaults = {
8
- blendColorPixelDataCircleMask,
9
- getCircleBrushOrPencilBounds,
10
- }
11
-
12
- type Deps = Partial<typeof defaults>
13
-
14
- /**
15
- * @param deps - @hidden
16
- */
17
- export const mutatorBlendColorCircleMask = ((writer: PixelWriter<any>, deps: Deps = defaults) => {
18
- const {
19
- blendColorPixelDataCircleMask = defaults.blendColorPixelDataCircleMask,
20
- getCircleBrushOrPencilBounds = defaults.getCircleBrushOrPencilBounds,
21
-
22
- } = deps
23
-
24
- const boundsOut: Rect = { x: 0, y: 0, w: 0, h: 0 }
25
-
26
- const blendColorPixelOptions: ColorBlendMaskOptions = {
27
- alpha: 255,
28
- blendFn: sourceOverPerfect,
29
- x: 0,
30
- y: 0,
31
- w: 0,
32
- h: 0,
33
- }
34
-
35
- return {
36
- applyCircleMask(
37
- color: Color32,
38
- centerX: number,
39
- centerY: number,
40
- brush: CircleMask,
41
- alpha = 255,
42
- blendFn?: BlendColor32,
43
- ): boolean {
44
-
45
- const target = writer.config.target
46
- const b = getCircleBrushOrPencilBounds(
47
- centerX,
48
- centerY,
49
- brush.size,
50
- target.width,
51
- target.height,
52
- boundsOut,
53
- )
54
-
55
- const didChange = writer.accumulator.storeRegionBeforeState(b.x, b.y, b.w, b.h)
56
- return didChange(
57
- blendColorPixelDataCircleMask(
58
- target,
59
- color,
60
- centerX,
61
- centerY,
62
- brush,
63
- alpha,
64
- blendFn,
65
- blendColorPixelOptions,
66
- b,
67
- ),
68
- )
69
- },
70
- }
71
- }) satisfies HistoryMutator<any, Deps>
@@ -1,32 +0,0 @@
1
- import { type CircleAlphaMask, MaskType } from '../_types'
2
-
3
- export function makeCircleAlphaMask(size: number, fallOff: (d: number) => number = () => 1): CircleAlphaMask {
4
- const area = size * size
5
- const data = new Uint8Array(area)
6
- const radius = size / 2
7
- const invR = 1 / radius
8
-
9
- const minOffset = -Math.ceil(radius - 0.5)
10
-
11
- for (let y = 0; y < size; y++) {
12
- for (let x = 0; x < size; x++) {
13
- const dx = x - radius + 0.5
14
- const dy = y - radius + 0.5
15
- const distSqr = dx * dx + dy * dy
16
- if (distSqr <= (radius * radius)) {
17
- const dist = Math.sqrt(distSqr)
18
- data[y * size + x] = (fallOff(1 - (dist * invR)) * 255) | 0
19
- }
20
- }
21
- }
22
-
23
- return {
24
- type: MaskType.ALPHA,
25
- data,
26
- w: size,
27
- h: size,
28
- radius,
29
- size,
30
- minOffset,
31
- }
32
- }
@@ -1,98 +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
- /**
6
- * Applies a rectangular brush to the pixel data.
7
- * @returns true if any pixels were actually modified.
8
- */
9
- export function applyRectBrushToPixelData(
10
- target: IPixelData,
11
- color: Color32,
12
- centerX: number,
13
- centerY: number,
14
- brushWidth: number,
15
- brushHeight: number,
16
- alpha = 255,
17
- fallOff: (dist: number) => number,
18
- blendFn: BlendColor32 = sourceOverPerfect,
19
- bounds?: Rect,
20
- ): boolean {
21
- const targetWidth = target.width
22
- const targetHeight = target.height
23
-
24
- const b = bounds ?? getRectBrushOrPencilBounds(
25
- centerX,
26
- centerY,
27
- brushWidth,
28
- brushHeight,
29
- targetWidth,
30
- targetHeight,
31
- )
32
-
33
- if (b.w <= 0 || b.h <= 0) {
34
- return false
35
- }
36
-
37
- const data32 = target.data32
38
- const baseColor = color & 0x00ffffff
39
- const baseSrcAlpha = color >>> 24
40
- const isOpaque = alpha === 255
41
-
42
- const invHalfW = 1 / (brushWidth / 2)
43
- const invHalfH = 1 / (brushHeight / 2)
44
-
45
- const centerOffsetX = (brushWidth % 2 === 0) ? 0.5 : 0
46
- const centerOffsetY = (brushHeight % 2 === 0) ? 0.5 : 0
47
- const fCenterX = Math.floor(centerX)
48
- const fCenterY = Math.floor(centerY)
49
-
50
- const endX = b.x + b.w
51
- const endY = b.y + b.h
52
- const isOverwrite = (blendFn as any).isOverwrite
53
- let didChange = false
54
-
55
- for (let py = b.y; py < endY; py++) {
56
- const rowOffset = py * targetWidth
57
- const dy = Math.abs((py - fCenterY) + centerOffsetY) * invHalfH
58
-
59
- for (let px = b.x; px < endX; px++) {
60
- const idx = rowOffset + px
61
- const dx = Math.abs((px - fCenterX) + centerOffsetX) * invHalfW
62
- const dist = dx > dy ? dx : dy
63
-
64
- const strength = fallOff(dist)
65
- const maskVal = (strength * 255) | 0
66
-
67
- if (maskVal <= 0) continue
68
-
69
- let weight = alpha
70
-
71
- if (isOpaque) {
72
- weight = maskVal
73
- } else if (maskVal !== 255) {
74
- weight = (maskVal * alpha + 128) >> 8
75
- }
76
-
77
- let finalCol = color
78
-
79
- if (weight < 255) {
80
- const a = (baseSrcAlpha * weight + 128) >> 8
81
-
82
- if (a === 0 && !isOverwrite) continue
83
-
84
- finalCol = ((a << 24) | baseColor) >>> 0 as Color32
85
- }
86
-
87
- const current = data32[idx] as Color32
88
- const next = blendFn(finalCol, current)
89
-
90
- if (current !== next) {
91
- data32[idx] = next
92
- didChange = true
93
- }
94
- }
95
- }
96
-
97
- return didChange
98
- }
@@ -1,92 +0,0 @@
1
- import {
2
- type BlendColor32,
3
- type CircleMask,
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 mask 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 blendColorPixelDataCircleMask(
29
- target: IPixelData,
30
- color: Color32,
31
- centerX: number,
32
- centerY: number,
33
- brush: CircleMask,
34
- alpha = 255,
35
- blendFn: BlendColor32 = sourceOverPerfect,
36
- scratchOptions: ColorBlendMaskOptions = {},
37
- bounds?: Rect,
38
- ): boolean {
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 false
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 false
63
-
64
- scratchOptions.x = ix
65
- scratchOptions.y = iy
66
- scratchOptions.w = iw
67
- scratchOptions.h = ih
68
- scratchOptions.mx = ix - unclippedStartX
69
- scratchOptions.my = iy - unclippedStartY
70
- scratchOptions.alpha = alpha
71
- scratchOptions.blendFn = blendFn
72
-
73
- if (brush.type === MaskType.ALPHA) {
74
- return blendColorPixelDataAlphaMask(
75
- target,
76
- color,
77
- brush,
78
- scratchOptions,
79
- )
80
- }
81
-
82
- if (brush.type === MaskType.BINARY) {
83
- return blendColorPixelDataBinaryMask(
84
- target,
85
- color,
86
- brush,
87
- scratchOptions,
88
- )
89
- }
90
-
91
- return false
92
- }
@@ -1,122 +0,0 @@
1
- import type { AlphaMaskRect, BinaryMaskRect, Color32, Rect } from '../_types'
2
- import type { PixelEngineConfig } from '../History/PixelEngineConfig'
3
- import type { PixelTile } from './PixelTile'
4
- import type { PixelTilePool } from './PixelTilePool'
5
-
6
- export class PaintBuffer {
7
- readonly lookup: (PixelTile | undefined)[]
8
-
9
- constructor(
10
- readonly config: PixelEngineConfig,
11
- readonly tilePool: PixelTilePool,
12
- ) {
13
- this.lookup = []
14
- }
15
-
16
- private processMaskTiles(
17
- mask: Rect,
18
- callback: (tile: PixelTile, bX: number, bY: number, bW: number, bH: number, mX: number, mY: number) => void,
19
- ): void {
20
- const { tileShift, targetColumns } = this.config
21
-
22
- const x1 = mask.x >> tileShift
23
- const y1 = mask.y >> tileShift
24
- const x2 = (mask.x + mask.w - 1) >> tileShift
25
- const y2 = (mask.y + mask.h - 1) >> tileShift
26
-
27
- for (let ty = y1; ty <= y2; ty++) {
28
- const tileRowIndex = ty * targetColumns
29
- const tileTop = ty << tileShift
30
-
31
- for (let tx = x1; tx <= x2; tx++) {
32
- const id = tileRowIndex + tx
33
- let tile = this.lookup[id]
34
-
35
- if (!tile) {
36
- tile = this.tilePool.getTile(id, tx, ty)
37
- this.lookup[id] = tile
38
- }
39
-
40
- const tileLeft = tx << tileShift
41
-
42
- const startX = Math.max(mask.x, tileLeft)
43
- const endX = Math.min(mask.x + mask.w, tileLeft + this.config.tileSize)
44
- const startY = Math.max(mask.y, tileTop)
45
- const endY = Math.min(mask.y + mask.h, tileTop + this.config.tileSize)
46
-
47
- // Passing 7 primitive arguments to avoid object allocation
48
- callback(
49
- tile,
50
- startX,
51
- startY,
52
- endX - startX,
53
- endY - startY,
54
- startX - mask.x,
55
- startY - mask.y,
56
- )
57
- }
58
- }
59
- }
60
-
61
- writeColorBinaryMaskRect(color: Color32, mask: BinaryMaskRect): void {
62
- const { tileShift, tileMask } = this.config
63
- const maskData = mask.data
64
- const maskW = mask.w
65
-
66
- this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
67
- const data32 = tile.data32
68
- const startTileX = bX & tileMask
69
-
70
- for (let i = 0; i < bH; i++) {
71
- const tileY = (bY + i) & tileMask
72
- const maskY = mY + i
73
- const tileRowOffset = tileY << tileShift
74
- const maskRowOffset = maskY * maskW
75
-
76
- const destStart = tileRowOffset + startTileX
77
- const maskStart = maskRowOffset + mX
78
-
79
- for (let j = 0; j < bW; j++) {
80
- if (maskData[maskStart + j]) {
81
- data32[destStart + j] = color
82
- }
83
- }
84
- }
85
- })
86
- }
87
-
88
- writeColorAlphaMaskRect(color: Color32, mask: AlphaMaskRect): void {
89
- const { tileShift, tileMask } = this.config
90
- const maskData = mask.data
91
- const maskW = mask.w
92
- const colorRGB = color & 0x00ffffff
93
- const colorA = color >>> 24
94
-
95
- this.processMaskTiles(mask, (tile, bX, bY, bW, bH, mX, mY) => {
96
- const data32 = tile.data32
97
- const startTileX = bX & tileMask
98
-
99
- for (let i = 0; i < bH; i++) {
100
- const tileY = (bY + i) & tileMask
101
- const maskY = mY + i
102
- const tileRowOffset = tileY << tileShift
103
- const maskRowOffset = maskY * maskW
104
-
105
- const destStart = tileRowOffset + startTileX
106
- const maskStart = maskRowOffset + mX
107
-
108
- for (let j = 0; j < bW; j++) {
109
- const maskA = maskData[maskStart + j]
110
- if (maskA > 0) {
111
- const finalA = (colorA * maskA + 128) >> 8
112
- data32[destStart + j] = (colorRGB | (finalA << 24)) >>> 0 as Color32
113
- }
114
- }
115
- }
116
- })
117
- }
118
-
119
- clear(): void {
120
- this.tilePool.releaseTiles(this.lookup)
121
- }
122
- }
@@ -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
- }