pixel-data-js 0.17.1 → 0.19.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.
- package/README.md +6 -1
- package/dist/index.dev.cjs +2747 -1397
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +2725 -1403
- package/dist/index.dev.js.map +1 -1
- package/dist/index.prod.cjs +2747 -1397
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +401 -241
- package/dist/index.prod.js +2725 -1403
- package/dist/index.prod.js.map +1 -1
- package/package.json +21 -6
- package/src/Algorithm/forEachLinePoint.ts +36 -0
- package/src/BlendModes/BlendModeRegistry.ts +2 -0
- package/src/BlendModes/blend-modes-fast.ts +2 -2
- package/src/BlendModes/blend-modes-perfect.ts +5 -4
- package/src/BlendModes/toBlendModeIndexAndName.ts +41 -0
- package/src/History/PixelAccumulator.ts +2 -2
- package/src/History/PixelMutator/mutatorApplyAlphaMask.ts +30 -0
- package/src/History/PixelMutator/mutatorApplyBinaryMask.ts +30 -0
- package/src/History/PixelMutator/mutatorApplyCircleBrush.ts +59 -0
- package/src/History/PixelMutator/mutatorApplyCircleBrushStroke.ts +138 -0
- package/src/History/PixelMutator/mutatorApplyCirclePencil.ts +59 -0
- package/src/History/PixelMutator/mutatorApplyCirclePencilStroke.ts +131 -0
- package/src/History/PixelMutator/mutatorApplyRectBrush.ts +61 -0
- package/src/History/PixelMutator/mutatorApplyRectBrushStroke.ts +169 -0
- package/src/History/PixelMutator/mutatorApplyRectPencil.ts +62 -0
- package/src/History/PixelMutator/mutatorApplyRectPencilStroke.ts +149 -0
- package/src/History/PixelMutator/mutatorBlendColor.ts +9 -4
- package/src/History/PixelMutator/mutatorBlendPixelData.ts +10 -5
- package/src/History/PixelMutator/mutatorClear.ts +27 -0
- package/src/History/PixelMutator/{mutatorFillPixelData.ts → mutatorFill.ts} +9 -3
- package/src/History/PixelMutator/mutatorInvert.ts +10 -3
- package/src/History/PixelMutator.ts +27 -3
- package/src/History/PixelPatchTiles.ts +2 -2
- package/src/History/PixelWriter.ts +7 -3
- package/src/ImageData/ImageDataLike.ts +13 -0
- package/src/ImageData/ReusableImageData.ts +1 -4
- package/src/ImageData/extractImageDataBuffer.ts +22 -15
- package/src/ImageData/serialization.ts +4 -4
- package/src/ImageData/uInt32ArrayToImageData.ts +29 -0
- package/src/ImageData/writeImageData.ts +26 -18
- package/src/ImageData/writeImageDataBuffer.ts +30 -18
- package/src/IndexedImage/indexedImageToAverageColor.ts +1 -1
- package/src/Internal/resolveClipping.ts +140 -0
- package/src/Mask/applyBinaryMaskToAlphaMask.ts +89 -0
- package/src/Mask/copyMask.ts +1 -3
- package/src/Mask/mergeAlphaMasks.ts +81 -0
- package/src/Mask/mergeBinaryMasks.ts +89 -0
- package/src/PixelData/PixelBuffer32.ts +28 -0
- package/src/PixelData/PixelData.ts +38 -33
- package/src/PixelData/applyAlphaMaskToPixelData.ts +119 -0
- package/src/PixelData/applyBinaryMaskToPixelData.ts +111 -0
- package/src/PixelData/applyCircleBrushToPixelData.ts +58 -28
- package/src/PixelData/applyRectBrushToPixelData.ts +56 -73
- package/src/PixelData/blendColorPixelData.ts +18 -111
- package/src/PixelData/blendColorPixelDataAlphaMask.ts +111 -0
- package/src/PixelData/blendColorPixelDataBinaryMask.ts +89 -0
- package/src/PixelData/blendPixelData.ts +19 -107
- package/src/PixelData/blendPixelDataAlphaMask.ts +149 -0
- package/src/PixelData/blendPixelDataBinaryMask.ts +133 -0
- package/src/PixelData/clearPixelData.ts +2 -3
- package/src/PixelData/extractPixelData.ts +4 -4
- package/src/PixelData/extractPixelDataBuffer.ts +38 -26
- package/src/PixelData/fillPixelData.ts +18 -20
- package/src/PixelData/invertPixelData.ts +13 -21
- package/src/PixelData/pixelDataToAlphaMask.ts +2 -3
- package/src/PixelData/reflectPixelData.ts +3 -3
- package/src/PixelData/resamplePixelData.ts +2 -6
- package/src/PixelData/writePixelDataBuffer.ts +34 -20
- package/src/Rect/getCircleBrushOrPencilBounds.ts +43 -0
- package/src/Rect/getCircleBrushOrPencilStrokeBounds.ts +24 -0
- package/src/Rect/getRectBrushOrPencilBounds.ts +38 -0
- package/src/Rect/getRectBrushOrPencilStrokeBounds.ts +26 -0
- package/src/_types.ts +49 -33
- package/src/index.ts +47 -11
- package/src/History/PixelMutator/mutatorApplyMask.ts +0 -20
- package/src/Mask/mergeMasks.ts +0 -100
- package/src/PixelData/applyMaskToPixelData.ts +0 -129
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { BinaryMask, Color32, IPixelData, PixelBlendMaskOptions } from '../_types'
|
|
2
|
+
import { sourceOverPerfect } from '../BlendModes/blend-modes-perfect'
|
|
3
|
+
|
|
4
|
+
export function blendPixelDataBinaryMask(
|
|
5
|
+
dst: IPixelData,
|
|
6
|
+
src: IPixelData,
|
|
7
|
+
binaryMask: BinaryMask,
|
|
8
|
+
opts: PixelBlendMaskOptions,
|
|
9
|
+
) {
|
|
10
|
+
const {
|
|
11
|
+
x: targetX = 0,
|
|
12
|
+
y: targetY = 0,
|
|
13
|
+
sx: sourceX = 0,
|
|
14
|
+
sy: sourceY = 0,
|
|
15
|
+
w: width = src.width,
|
|
16
|
+
h: height = src.height,
|
|
17
|
+
alpha: globalAlpha = 255,
|
|
18
|
+
blendFn = sourceOverPerfect,
|
|
19
|
+
mw = src.width,
|
|
20
|
+
mx = 0,
|
|
21
|
+
my = 0,
|
|
22
|
+
invertMask = false,
|
|
23
|
+
} = opts
|
|
24
|
+
|
|
25
|
+
if (globalAlpha === 0) return
|
|
26
|
+
|
|
27
|
+
let x = targetX
|
|
28
|
+
let y = targetY
|
|
29
|
+
let sx = sourceX
|
|
30
|
+
let sy = sourceY
|
|
31
|
+
let w = width
|
|
32
|
+
let h = height
|
|
33
|
+
|
|
34
|
+
// 1. Source Clipping
|
|
35
|
+
if (sx < 0) {
|
|
36
|
+
x -= sx
|
|
37
|
+
w += sx
|
|
38
|
+
sx = 0
|
|
39
|
+
}
|
|
40
|
+
if (sy < 0) {
|
|
41
|
+
y -= sy
|
|
42
|
+
h += sy
|
|
43
|
+
sy = 0
|
|
44
|
+
}
|
|
45
|
+
w = Math.min(w, src.width - sx)
|
|
46
|
+
h = Math.min(h, src.height - sy)
|
|
47
|
+
|
|
48
|
+
// 2. Destination Clipping
|
|
49
|
+
if (x < 0) {
|
|
50
|
+
sx -= x
|
|
51
|
+
w += x
|
|
52
|
+
x = 0
|
|
53
|
+
}
|
|
54
|
+
if (y < 0) {
|
|
55
|
+
sy -= y
|
|
56
|
+
h += y
|
|
57
|
+
y = 0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const actualW = Math.min(w, dst.width - x)
|
|
61
|
+
const actualH = Math.min(h, dst.height - y)
|
|
62
|
+
|
|
63
|
+
if (actualW <= 0 || actualH <= 0) return
|
|
64
|
+
|
|
65
|
+
// 3. Coordinate Displacement for Mask Sync
|
|
66
|
+
// dx/dy represents how far the clipped start is from the requested start.
|
|
67
|
+
// This is the stable way to align the mask across all clipping permutations.
|
|
68
|
+
const dx = (x - targetX) | 0
|
|
69
|
+
const dy = (y - targetY) | 0
|
|
70
|
+
|
|
71
|
+
const dst32 = dst.data32
|
|
72
|
+
const src32 = src.data32
|
|
73
|
+
const dw = dst.width
|
|
74
|
+
const sw = src.width
|
|
75
|
+
const mPitch = mw
|
|
76
|
+
|
|
77
|
+
let dIdx = (y * dw + x) | 0
|
|
78
|
+
let sIdx = (sy * sw + sx) | 0
|
|
79
|
+
let mIdx = ((my + dy) * mPitch + (mx + dx)) | 0
|
|
80
|
+
|
|
81
|
+
const dStride = (dw - actualW) | 0
|
|
82
|
+
const sStride = (sw - actualW) | 0
|
|
83
|
+
const mStride = (mPitch - actualW) | 0
|
|
84
|
+
|
|
85
|
+
const skipVal = invertMask ? 1 : 0
|
|
86
|
+
const isOpaque = globalAlpha === 255
|
|
87
|
+
const isOverwrite = blendFn.isOverwrite || false
|
|
88
|
+
|
|
89
|
+
for (let iy = 0; iy < actualH; iy++) {
|
|
90
|
+
for (let ix = 0; ix < actualW; ix++) {
|
|
91
|
+
// Binary Mask Check (Earliest exit)
|
|
92
|
+
if (binaryMask[mIdx] === skipVal) {
|
|
93
|
+
dIdx++
|
|
94
|
+
sIdx++
|
|
95
|
+
mIdx++
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const srcCol = src32[sIdx] as Color32
|
|
100
|
+
const srcAlpha = srcCol >>> 24
|
|
101
|
+
|
|
102
|
+
// Source Alpha Check
|
|
103
|
+
if (srcAlpha === 0 && !isOverwrite) {
|
|
104
|
+
dIdx++
|
|
105
|
+
sIdx++
|
|
106
|
+
mIdx++
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let finalCol = srcCol
|
|
111
|
+
if (!isOpaque) {
|
|
112
|
+
// Rounding-corrected global alpha application
|
|
113
|
+
const a = (srcAlpha * globalAlpha + 128) >> 8
|
|
114
|
+
if (a === 0 && !isOverwrite) {
|
|
115
|
+
dIdx++
|
|
116
|
+
sIdx++
|
|
117
|
+
mIdx++
|
|
118
|
+
continue
|
|
119
|
+
}
|
|
120
|
+
finalCol = ((srcCol & 0x00ffffff) | (a << 24)) >>> 0 as Color32
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
dst32[dIdx] = blendFn(finalCol, dst32[dIdx] as Color32)
|
|
124
|
+
|
|
125
|
+
dIdx++
|
|
126
|
+
sIdx++
|
|
127
|
+
mIdx++
|
|
128
|
+
}
|
|
129
|
+
dIdx += dStride
|
|
130
|
+
sIdx += sStride
|
|
131
|
+
mIdx += mStride
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Color32, Rect } from '../_types'
|
|
2
|
-
import type { PixelData } from './PixelData'
|
|
1
|
+
import type { Color32, IPixelData, Rect } from '../_types'
|
|
3
2
|
import { fillPixelData } from './fillPixelData'
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -7,7 +6,7 @@ import { fillPixelData } from './fillPixelData'
|
|
|
7
6
|
* Internally uses the optimized fillPixelData.
|
|
8
7
|
*/
|
|
9
8
|
export function clearPixelData(
|
|
10
|
-
dst:
|
|
9
|
+
dst: IPixelData,
|
|
11
10
|
rect?: Partial<Rect>,
|
|
12
11
|
): void {
|
|
13
12
|
fillPixelData(dst, 0 as Color32, rect)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Rect } from '../_types'
|
|
1
|
+
import type { IPixelData, Rect } from '../_types'
|
|
2
2
|
import { extractPixelDataBuffer } from './extractPixelDataBuffer'
|
|
3
3
|
import { PixelData } from './PixelData'
|
|
4
4
|
|
|
@@ -6,10 +6,10 @@ import { PixelData } from './PixelData'
|
|
|
6
6
|
* High-level extraction that returns a new PixelData instance.
|
|
7
7
|
* Leverages extractPixelDataBuffer for optimized 32-bit memory moves.
|
|
8
8
|
*/
|
|
9
|
-
export function extractPixelData(source:
|
|
10
|
-
export function extractPixelData(source:
|
|
9
|
+
export function extractPixelData(source: IPixelData, rect: Rect): PixelData
|
|
10
|
+
export function extractPixelData(source: IPixelData, x: number, y: number, w: number, h: number): PixelData
|
|
11
11
|
export function extractPixelData(
|
|
12
|
-
source:
|
|
12
|
+
source: IPixelData,
|
|
13
13
|
_x: Rect | number,
|
|
14
14
|
_y?: number,
|
|
15
15
|
_w?: number,
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type { Rect } from '../_types'
|
|
2
|
-
import
|
|
1
|
+
import type { IPixelData, Rect } from '../_types'
|
|
2
|
+
import { makeClippedBlit, resolveBlitClipping } from '../Internal/resolveClipping'
|
|
3
|
+
|
|
4
|
+
const SCRATCH_BLIT = makeClippedBlit()
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Extracts a rectangular region of pixels from PixelData.
|
|
6
8
|
* Returns a new Uint32Array containing the extracted pixels.
|
|
7
9
|
*/
|
|
8
|
-
export function extractPixelDataBuffer(source:
|
|
9
|
-
export function extractPixelDataBuffer(source:
|
|
10
|
+
export function extractPixelDataBuffer(source: IPixelData, rect: Rect): Uint32Array
|
|
11
|
+
export function extractPixelDataBuffer(source: IPixelData, x: number, y: number, w: number, h: number): Uint32Array
|
|
10
12
|
export function extractPixelDataBuffer(
|
|
11
|
-
source:
|
|
13
|
+
source: IPixelData,
|
|
12
14
|
_x: Rect | number,
|
|
13
15
|
_y?: number,
|
|
14
16
|
_w?: number,
|
|
@@ -27,33 +29,43 @@ export function extractPixelDataBuffer(
|
|
|
27
29
|
return new Uint32Array(0)
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
const dstImageData = new ImageData(w, h)
|
|
32
|
-
const dstData = new Uint32Array(dstImageData.data.buffer)
|
|
33
|
-
|
|
34
|
-
const x0 = Math.max(0, x)
|
|
35
|
-
const y0 = Math.max(0, y)
|
|
36
|
-
const x1 = Math.min(srcW, x + w)
|
|
37
|
-
const y1 = Math.min(srcH, y + h)
|
|
32
|
+
const dstData = new Uint32Array(w * h)
|
|
38
33
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
// We map from Source (srcW, srcH) at (x,y)
|
|
35
|
+
// To Dest (w, h) at (0,0)
|
|
36
|
+
// Note: resolveBlitClipping usually takes (dstX, dstY, srcX, srcY...)
|
|
37
|
+
// Here we are "blitting" FROM x,y TO 0,0.
|
|
38
|
+
const clip = resolveBlitClipping(
|
|
39
|
+
0,
|
|
40
|
+
0,
|
|
41
|
+
x,
|
|
42
|
+
y,
|
|
43
|
+
w,
|
|
44
|
+
h,
|
|
45
|
+
w,
|
|
46
|
+
h,
|
|
47
|
+
srcW,
|
|
48
|
+
srcH,
|
|
49
|
+
SCRATCH_BLIT,
|
|
50
|
+
)
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
const copyHeight = y1 - y0
|
|
52
|
+
if (!clip.inBounds) return dstData
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
const {
|
|
55
|
+
x: dstX,
|
|
56
|
+
y: dstY,
|
|
57
|
+
sx: srcX,
|
|
58
|
+
sy: srcY,
|
|
59
|
+
w: copyW,
|
|
60
|
+
h: copyH,
|
|
61
|
+
} = clip
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const dstStart =
|
|
63
|
+
for (let row = 0; row < copyH; row++) {
|
|
64
|
+
const srcStart = (srcY + row) * srcW + srcX
|
|
65
|
+
const dstStart = (dstY + row) * w + dstX
|
|
54
66
|
|
|
55
67
|
// Perform the high-speed 32-bit bulk copy
|
|
56
|
-
const chunk = srcData.subarray(srcStart, srcStart +
|
|
68
|
+
const chunk = srcData.subarray(srcStart, srcStart + copyW)
|
|
57
69
|
dstData.set(chunk, dstStart)
|
|
58
70
|
}
|
|
59
71
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type { Color32, Rect } from '../_types'
|
|
1
|
+
import type { Color32, IPixelData, Rect } from '../_types'
|
|
2
|
+
import { makeClippedRect, resolveRectClipping } from '../Internal/resolveClipping'
|
|
2
3
|
import type { PixelData } from './PixelData'
|
|
3
4
|
|
|
5
|
+
const SCRATCH_RECT = makeClippedRect()
|
|
6
|
+
|
|
4
7
|
/**
|
|
5
8
|
* Fills a region or the {@link PixelData} buffer with a solid color.
|
|
6
9
|
*
|
|
@@ -10,7 +13,7 @@ import type { PixelData } from './PixelData'
|
|
|
10
13
|
* buffer is filled.
|
|
11
14
|
*/
|
|
12
15
|
export function fillPixelData(
|
|
13
|
-
dst:
|
|
16
|
+
dst: IPixelData,
|
|
14
17
|
color: Color32,
|
|
15
18
|
rect?: Partial<Rect>,
|
|
16
19
|
): void
|
|
@@ -23,7 +26,7 @@ export function fillPixelData(
|
|
|
23
26
|
* @param h - Height of the fill area.
|
|
24
27
|
*/
|
|
25
28
|
export function fillPixelData(
|
|
26
|
-
dst:
|
|
29
|
+
dst: IPixelData,
|
|
27
30
|
color: Color32,
|
|
28
31
|
x: number,
|
|
29
32
|
y: number,
|
|
@@ -31,7 +34,7 @@ export function fillPixelData(
|
|
|
31
34
|
h: number,
|
|
32
35
|
): void
|
|
33
36
|
export function fillPixelData(
|
|
34
|
-
dst:
|
|
37
|
+
dst: IPixelData,
|
|
35
38
|
color: Color32,
|
|
36
39
|
_x?: Partial<Rect> | number,
|
|
37
40
|
_y?: number,
|
|
@@ -60,35 +63,30 @@ export function fillPixelData(
|
|
|
60
63
|
h = dst.height
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
if (x < 0) {
|
|
65
|
-
w += x
|
|
66
|
-
x = 0
|
|
67
|
-
}
|
|
68
|
-
if (y < 0) {
|
|
69
|
-
h += y
|
|
70
|
-
y = 0
|
|
71
|
-
}
|
|
66
|
+
const clip = resolveRectClipping(x, y, w, h, dst.width, dst.height, SCRATCH_RECT)
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
const actualH = Math.min(h, dst.height - y)
|
|
68
|
+
if (!clip.inBounds) return
|
|
75
69
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
// Use the clipped values
|
|
71
|
+
const {
|
|
72
|
+
x: finalX,
|
|
73
|
+
y: finalY,
|
|
74
|
+
w: actualW,
|
|
75
|
+
h: actualH,
|
|
76
|
+
} = clip
|
|
79
77
|
|
|
80
78
|
const dst32 = dst.data32
|
|
81
79
|
const dw = dst.width
|
|
82
80
|
|
|
83
81
|
// Optimization: If filling the entire buffer, use the native .fill()
|
|
84
|
-
if (actualW === dw && actualH === dst.height &&
|
|
82
|
+
if (actualW === dw && actualH === dst.height && finalX === 0 && finalY === 0) {
|
|
85
83
|
dst32.fill(color)
|
|
86
84
|
return
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
// Row-by-row fill for partial rectangles
|
|
90
88
|
for (let iy = 0; iy < actualH; iy++) {
|
|
91
|
-
const start = (
|
|
89
|
+
const start = (finalY + iy) * dw + finalX
|
|
92
90
|
const end = start + actualW
|
|
93
91
|
dst32.fill(color, start, end)
|
|
94
92
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { type PixelMutateOptions } from '../_types'
|
|
2
|
-
import
|
|
1
|
+
import { type IPixelData, type PixelMutateOptions } from '../_types'
|
|
2
|
+
import { makeClippedRect, resolveRectClipping } from '../Internal/resolveClipping'
|
|
3
|
+
|
|
4
|
+
const SCRATCH_RECT = makeClippedRect()
|
|
3
5
|
|
|
4
6
|
export function invertPixelData(
|
|
5
|
-
pixelData:
|
|
7
|
+
pixelData: IPixelData,
|
|
6
8
|
opts: PixelMutateOptions = {},
|
|
7
9
|
): void {
|
|
8
10
|
const dst = pixelData
|
|
9
11
|
const {
|
|
10
12
|
x: targetX = 0,
|
|
11
13
|
y: targetY = 0,
|
|
12
|
-
|
|
13
14
|
w: width = pixelData.width,
|
|
14
15
|
h: height = pixelData.height,
|
|
15
16
|
mask,
|
|
@@ -19,25 +20,16 @@ export function invertPixelData(
|
|
|
19
20
|
invertMask = false,
|
|
20
21
|
} = opts
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
let y = targetY
|
|
24
|
-
let w = width
|
|
25
|
-
let h = height
|
|
26
|
-
|
|
27
|
-
// Destination Clipping
|
|
28
|
-
if (x < 0) {
|
|
29
|
-
w += x
|
|
30
|
-
x = 0
|
|
31
|
-
}
|
|
32
|
-
if (y < 0) {
|
|
33
|
-
h += y
|
|
34
|
-
y = 0
|
|
35
|
-
}
|
|
23
|
+
const clip = resolveRectClipping(targetX, targetY, width, height, dst.width, dst.height, SCRATCH_RECT)
|
|
36
24
|
|
|
37
|
-
|
|
38
|
-
const actualH = Math.min(h, dst.height - y)
|
|
25
|
+
if (!clip.inBounds) return
|
|
39
26
|
|
|
40
|
-
|
|
27
|
+
const {
|
|
28
|
+
x,
|
|
29
|
+
y,
|
|
30
|
+
w: actualW,
|
|
31
|
+
h: actualH,
|
|
32
|
+
} = clip
|
|
41
33
|
|
|
42
34
|
const dst32 = dst.data32
|
|
43
35
|
const dw = dst.width
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import type { AlphaMask } from '../_types'
|
|
2
|
-
import type { PixelData } from './PixelData'
|
|
1
|
+
import type { AlphaMask, IPixelData } from '../_types'
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Extracts the alpha channel from PixelData into a single-channel mask.
|
|
6
5
|
* Returns a Uint8Array branded as AlphaMask.
|
|
7
6
|
*/
|
|
8
7
|
export function pixelDataToAlphaMask(
|
|
9
|
-
pixelData:
|
|
8
|
+
pixelData: IPixelData,
|
|
10
9
|
): AlphaMask {
|
|
11
10
|
const {
|
|
12
11
|
data32,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IPixelData } from '../_types'
|
|
2
2
|
|
|
3
|
-
export function reflectPixelDataHorizontal(pixelData:
|
|
3
|
+
export function reflectPixelDataHorizontal(pixelData: IPixelData): void {
|
|
4
4
|
const width = pixelData.width
|
|
5
5
|
const height = pixelData.height
|
|
6
6
|
const data = pixelData.data32
|
|
@@ -20,7 +20,7 @@ export function reflectPixelDataHorizontal(pixelData: PixelData): void {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export function reflectPixelDataVertical(pixelData:
|
|
23
|
+
export function reflectPixelDataVertical(pixelData: IPixelData): void {
|
|
24
24
|
const width = pixelData.width
|
|
25
25
|
const height = pixelData.height
|
|
26
26
|
const data = pixelData.data32
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Resamples an PixelData by a specific factor using nearest neighbor
|
|
3
|
-
* Factor > 1 upscales, Factor < 1 downscales.
|
|
4
|
-
*/
|
|
5
|
-
import { PixelData } from '../index'
|
|
1
|
+
import { type IPixelData, PixelData } from '../index'
|
|
6
2
|
import { resample32 } from '../Internal/resample32'
|
|
7
3
|
|
|
8
4
|
/**
|
|
@@ -10,7 +6,7 @@ import { resample32 } from '../Internal/resample32'
|
|
|
10
6
|
* Factor > 1 upscales, Factor < 1 downscales.
|
|
11
7
|
*/
|
|
12
8
|
export function resamplePixelData(
|
|
13
|
-
pixelData:
|
|
9
|
+
pixelData: IPixelData,
|
|
14
10
|
factor: number,
|
|
15
11
|
): PixelData {
|
|
16
12
|
const { data, width, height } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { type IPixelData, type Rect } from '../_types'
|
|
2
|
+
import { makeClippedBlit, resolveBlitClipping } from '../Internal/resolveClipping'
|
|
1
3
|
import type { PixelData } from './PixelData'
|
|
2
|
-
|
|
4
|
+
|
|
5
|
+
const SCRATCH_BLIT = makeClippedBlit()
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Copies a pixel buffer into a specific region of a {@link PixelData} object.
|
|
@@ -8,12 +11,12 @@ import { type Rect } from '../_types'
|
|
|
8
11
|
* into the target {@link PixelData} buffer.
|
|
9
12
|
*/
|
|
10
13
|
export function writePixelDataBuffer(
|
|
11
|
-
target:
|
|
14
|
+
target: IPixelData,
|
|
12
15
|
data: Uint32Array,
|
|
13
16
|
rect: Rect,
|
|
14
17
|
): void
|
|
15
18
|
export function writePixelDataBuffer(
|
|
16
|
-
target:
|
|
19
|
+
target: IPixelData,
|
|
17
20
|
data: Uint32Array,
|
|
18
21
|
x: number,
|
|
19
22
|
y: number,
|
|
@@ -21,7 +24,7 @@ export function writePixelDataBuffer(
|
|
|
21
24
|
h: number,
|
|
22
25
|
): void
|
|
23
26
|
export function writePixelDataBuffer(
|
|
24
|
-
target:
|
|
27
|
+
target: IPixelData,
|
|
25
28
|
data: Uint32Array,
|
|
26
29
|
_x: Rect | number,
|
|
27
30
|
_y?: number,
|
|
@@ -41,25 +44,36 @@ export function writePixelDataBuffer(
|
|
|
41
44
|
const dstH = target.height
|
|
42
45
|
const dstData = target.data32
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
// treat the source buffer as a Source Image starting at 0,0 with size w,h
|
|
48
|
+
const clip = resolveBlitClipping(
|
|
49
|
+
x,
|
|
50
|
+
y,
|
|
51
|
+
0,
|
|
52
|
+
0,
|
|
53
|
+
w,
|
|
54
|
+
h,
|
|
55
|
+
dstW,
|
|
56
|
+
dstH,
|
|
57
|
+
w,
|
|
58
|
+
h,
|
|
59
|
+
SCRATCH_BLIT,
|
|
60
|
+
)
|
|
48
61
|
|
|
49
|
-
if (
|
|
50
|
-
return
|
|
51
|
-
}
|
|
62
|
+
if (!clip.inBounds) return
|
|
52
63
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
const {
|
|
65
|
+
x: dstX,
|
|
66
|
+
y: dstY,
|
|
67
|
+
sx: srcX,
|
|
68
|
+
sy: srcY,
|
|
69
|
+
w: copyW,
|
|
70
|
+
h: copyH,
|
|
71
|
+
} = clip
|
|
57
72
|
|
|
58
|
-
for (let row = 0; row <
|
|
59
|
-
const dstStart = (
|
|
60
|
-
const
|
|
61
|
-
const srcStart = srcRow * w + srcCol
|
|
73
|
+
for (let row = 0; row < copyH; row++) {
|
|
74
|
+
const dstStart = (dstY + row) * dstW + dstX
|
|
75
|
+
const srcStart = (srcY + row) * w + srcX
|
|
62
76
|
|
|
63
|
-
dstData.set(data.subarray(srcStart, srcStart +
|
|
77
|
+
dstData.set(data.subarray(srcStart, srcStart + copyW), dstStart)
|
|
64
78
|
}
|
|
65
79
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
}
|