pixel-data-js 0.31.0 → 0.33.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/dist/index.prod.cjs +433 -310
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +78 -52
- package/dist/index.prod.js +430 -306
- package/dist/index.prod.js.map +1 -1
- package/package.json +1 -1
- package/src/ImageData/copyImageData.ts +4 -2
- package/src/ImageData/extractImageData.ts +54 -0
- package/src/ImageData/extractImageDataBuffer.ts +55 -28
- package/src/ImageData/writeImageData.ts +47 -69
- package/src/ImageData/writeImageDataBuffer.ts +77 -41
- package/src/PixelData/extractPixelDataBuffer.ts +51 -40
- package/src/PixelData/fillPixelData.ts +45 -28
- package/src/PixelData/fillPixelDataBinaryMask.ts +43 -38
- package/src/PixelData/fillPixelDataFast.ts +25 -16
- package/src/PixelData/invertPixelData.ts +38 -21
- package/src/PixelData/resizePixelData.ts +75 -0
- package/src/PixelData/writePixelData.ts +55 -0
- package/src/PixelData/writePixelDataBuffer.ts +53 -38
- package/src/index.ts +3 -1
- package/src/Rect/resolveClipping.ts +0 -140
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ImageDataLike } from './_ImageData-types'
|
|
2
2
|
|
|
3
|
-
export function copyImageData(
|
|
4
|
-
|
|
3
|
+
export function copyImageData(source: ImageDataLike): ImageData {
|
|
4
|
+
const dataCopy = new Uint8ClampedArray(source.data)
|
|
5
|
+
|
|
6
|
+
return new ImageData(dataCopy, source.width, source.height)
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
export function copyImageDataLike({ data, width, height }: ImageDataLike): ImageDataLike {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Rect } from '../Rect/_rect-types'
|
|
2
|
+
import type { ImageDataLike } from './_ImageData-types'
|
|
3
|
+
import { extractImageDataBuffer } from './extractImageDataBuffer'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extracts a specific rectangular region of pixels from a larger {@link ImageDataLike}
|
|
7
|
+
* source into a new {@link Uint8ClampedArray}.
|
|
8
|
+
*
|
|
9
|
+
* This is a "read-only" operation that returns a copy of the pixel data.
|
|
10
|
+
*
|
|
11
|
+
* @param imageData - The source image data to read from.
|
|
12
|
+
* @param rect - A rect defining the region to extract.
|
|
13
|
+
* @returns A buffer containing the RGBA pixel data of the region.
|
|
14
|
+
*/
|
|
15
|
+
export function extractImageData(
|
|
16
|
+
imageData: ImageDataLike,
|
|
17
|
+
rect: Rect,
|
|
18
|
+
): ImageData | null
|
|
19
|
+
/**
|
|
20
|
+
* @param imageData - The source image data to read from.
|
|
21
|
+
* @param x - The starting horizontal coordinate.
|
|
22
|
+
* @param y - The starting vertical coordinate.
|
|
23
|
+
* @param w - The width of the region to extract.
|
|
24
|
+
* @param h - The height of the region to extract.
|
|
25
|
+
* @returns A buffer containing the RGBA pixel data of the region.
|
|
26
|
+
*/
|
|
27
|
+
export function extractImageData(
|
|
28
|
+
imageData: ImageDataLike,
|
|
29
|
+
x: number,
|
|
30
|
+
y: number,
|
|
31
|
+
w: number,
|
|
32
|
+
h: number,
|
|
33
|
+
): ImageData | null
|
|
34
|
+
export function extractImageData(
|
|
35
|
+
imageData: ImageDataLike,
|
|
36
|
+
_x: Rect | number,
|
|
37
|
+
_y?: number,
|
|
38
|
+
_w?: number,
|
|
39
|
+
_h?: number,
|
|
40
|
+
): ImageData | null {
|
|
41
|
+
const { x, y, w, h } = typeof _x === 'object'
|
|
42
|
+
? _x
|
|
43
|
+
: { x: _x, y: _y!, w: _w!, h: _h! }
|
|
44
|
+
|
|
45
|
+
if (w <= 0 || h <= 0) return null
|
|
46
|
+
|
|
47
|
+
const result = new ImageData(w, h)
|
|
48
|
+
|
|
49
|
+
const buffer = extractImageDataBuffer(imageData, x, y, w, h)
|
|
50
|
+
result.data.set(buffer)
|
|
51
|
+
|
|
52
|
+
return result
|
|
53
|
+
|
|
54
|
+
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { Rect } from '../Rect/_rect-types'
|
|
2
|
-
import { makeClippedBlit, resolveBlitClipping } from '../Rect/resolveClipping'
|
|
3
2
|
import type { ImageDataLike } from './_ImageData-types'
|
|
4
3
|
|
|
5
|
-
const SCRATCH_BLIT = makeClippedBlit()
|
|
6
|
-
|
|
7
4
|
/**
|
|
8
5
|
* Extracts a specific rectangular region of pixels from a larger {@link ImageDataLike}
|
|
9
6
|
* source into a new {@link Uint8ClampedArray}.
|
|
@@ -43,37 +40,67 @@ export function extractImageDataBuffer(
|
|
|
43
40
|
const { x, y, w, h } = typeof _x === 'object'
|
|
44
41
|
? _x
|
|
45
42
|
: { x: _x, y: _y!, w: _w!, h: _h! }
|
|
46
|
-
|
|
47
|
-
const { width: srcW, height: srcH, data: src } = imageData
|
|
48
|
-
// Safety check for invalid dimensions
|
|
49
43
|
if (w <= 0 || h <= 0) return new Uint8ClampedArray(0)
|
|
50
|
-
const out = new Uint8ClampedArray(w * h * 4)
|
|
51
44
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
45
|
+
const srcW = imageData.width
|
|
46
|
+
const srcH = imageData.height
|
|
47
|
+
const src = imageData.data
|
|
48
|
+
|
|
49
|
+
const outLen = w * h * 4
|
|
50
|
+
const out = new Uint8ClampedArray(outLen)
|
|
51
|
+
|
|
52
|
+
let srcX = x
|
|
53
|
+
let srcY = y
|
|
54
|
+
let dstX = 0
|
|
55
|
+
let dstY = 0
|
|
56
|
+
let copyW = w
|
|
57
|
+
let copyH = h
|
|
58
|
+
|
|
59
|
+
if (srcX < 0) {
|
|
60
|
+
dstX = -srcX
|
|
61
|
+
copyW += srcX
|
|
62
|
+
srcX = 0
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (srcY < 0) {
|
|
66
|
+
dstY = -srcY
|
|
67
|
+
copyH += srcY
|
|
68
|
+
srcY = 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
copyW = Math.min(copyW, srcW - srcX)
|
|
72
|
+
copyH = Math.min(copyH, srcH - srcY)
|
|
73
|
+
|
|
74
|
+
if (copyW <= 0 || copyH <= 0) return out
|
|
75
|
+
|
|
76
|
+
// 2. Perform high-speed block copy
|
|
77
|
+
// Attempt to use a 32-bit view if the buffer is memory-aligned.
|
|
78
|
+
// This reduces loop iterations and arithmetic by 4x.
|
|
79
|
+
const isAligned = src.byteOffset % 4 === 0
|
|
80
|
+
|
|
81
|
+
if (isAligned) {
|
|
82
|
+
const srcLen32 = src.byteLength / 4
|
|
83
|
+
const src32 = new Uint32Array(src.buffer, src.byteOffset, srcLen32)
|
|
84
|
+
const out32 = new Uint32Array(out.buffer)
|
|
65
85
|
|
|
66
|
-
|
|
86
|
+
for (let row = 0; row < copyH; row++) {
|
|
87
|
+
const srcStart = (srcY + row) * srcW + srcX
|
|
88
|
+
const dstStart = (dstY + row) * w + dstX
|
|
89
|
+
const chunk = src32.subarray(srcStart, srcStart + copyW)
|
|
67
90
|
|
|
68
|
-
|
|
69
|
-
|
|
91
|
+
out32.set(chunk, dstStart)
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
// Fallback for unaligned data
|
|
95
|
+
const rowLen = copyW * 4
|
|
70
96
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
97
|
+
for (let row = 0; row < copyH; row++) {
|
|
98
|
+
const srcStart = ((srcY + row) * srcW + srcX) * 4
|
|
99
|
+
const dstStart = ((dstY + row) * w + dstX) * 4
|
|
100
|
+
const chunk = src.subarray(srcStart, srcStart + rowLen)
|
|
74
101
|
|
|
75
|
-
|
|
76
|
-
|
|
102
|
+
out.set(chunk, dstStart)
|
|
103
|
+
}
|
|
77
104
|
}
|
|
78
105
|
|
|
79
106
|
return out
|
|
@@ -1,97 +1,75 @@
|
|
|
1
|
-
import { MaskType } from '../Mask/_mask-types'
|
|
2
|
-
import { makeClippedBlit, resolveBlitClipping } from '../Rect/resolveClipping'
|
|
3
|
-
|
|
4
|
-
const SCRATCH_BLIT = makeClippedBlit()
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
|
-
* Writes image data from a source to a target
|
|
2
|
+
* Writes image data from a source to a target.
|
|
8
3
|
*
|
|
9
4
|
* @param target - The destination ImageData to write to.
|
|
10
5
|
* @param source - The source ImageData to read from.
|
|
11
6
|
* @param x - The x-coordinate in the target where drawing starts.
|
|
12
7
|
* @param y - The y-coordinate in the target where drawing starts.
|
|
13
|
-
* @param sx - The x-coordinate in the source to start copying from.
|
|
14
|
-
* @param sy - The y-coordinate in the source to start copying from.
|
|
15
|
-
* @param sw - The width of the rectangle to copy.
|
|
16
|
-
* @param sh - The height of the rectangle to copy.
|
|
17
|
-
* @param mask - An optional Uint8Array mask (0-255). 0 is transparent, 255 is opaque.
|
|
18
|
-
* @param maskType - type of mask
|
|
19
8
|
*/
|
|
20
9
|
export function writeImageData(
|
|
21
10
|
target: ImageData,
|
|
22
11
|
source: ImageData,
|
|
23
|
-
x
|
|
24
|
-
y
|
|
25
|
-
sx: number = 0,
|
|
26
|
-
sy: number = 0,
|
|
27
|
-
sw: number = source.width,
|
|
28
|
-
sh: number = source.height,
|
|
29
|
-
mask: Uint8Array | null = null,
|
|
30
|
-
maskType: MaskType = MaskType.BINARY,
|
|
12
|
+
x = 0,
|
|
13
|
+
y = 0,
|
|
31
14
|
): void {
|
|
32
15
|
const dstW = target.width
|
|
33
16
|
const dstH = target.height
|
|
34
|
-
const
|
|
17
|
+
const dst = target.data
|
|
18
|
+
|
|
35
19
|
const srcW = source.width
|
|
36
|
-
const
|
|
20
|
+
const srcH = source.height
|
|
21
|
+
const src = source.data
|
|
37
22
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
23
|
+
let dstX = x
|
|
24
|
+
let dstY = y
|
|
25
|
+
let srcX = 0
|
|
26
|
+
let srcY = 0
|
|
27
|
+
let copyW = srcW
|
|
28
|
+
let copyH = srcH
|
|
43
29
|
|
|
44
|
-
if (
|
|
30
|
+
if (dstX < 0) {
|
|
31
|
+
srcX = -dstX
|
|
32
|
+
copyW += dstX
|
|
33
|
+
dstX = 0
|
|
34
|
+
}
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
36
|
+
if (dstY < 0) {
|
|
37
|
+
srcY = -dstY
|
|
38
|
+
copyH += dstY
|
|
39
|
+
dstY = 0
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
copyW = Math.min(copyW, dstW - dstX)
|
|
43
|
+
copyH = Math.min(copyH, dstH - dstY)
|
|
54
44
|
|
|
55
|
-
|
|
45
|
+
if (copyW <= 0 || copyH <= 0) return
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const currentSrcY = srcY + row
|
|
47
|
+
const isDstAligned = dst.byteOffset % 4 === 0
|
|
48
|
+
const isSrcAligned = src.byteOffset % 4 === 0
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
const
|
|
50
|
+
if (isDstAligned && isSrcAligned) {
|
|
51
|
+
const dstLen32 = dst.byteLength / 4
|
|
52
|
+
const dst32 = new Uint32Array(dst.buffer, dst.byteOffset, dstLen32)
|
|
63
53
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const mi = currentSrcY * srcW + (srcX + ix)
|
|
67
|
-
const alpha = mask[mi]
|
|
54
|
+
const srcLen32 = src.byteLength / 4
|
|
55
|
+
const src32 = new Uint32Array(src.buffer, src.byteOffset, srcLen32)
|
|
68
56
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
57
|
+
for (let row = 0; row < copyH; row++) {
|
|
58
|
+
const dstStart = (dstY + row) * dstW + dstX
|
|
59
|
+
const srcStart = (srcY + row) * srcW + srcX
|
|
60
|
+
const chunk = src32.subarray(srcStart, srcStart + copyW)
|
|
72
61
|
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
dst32.set(chunk, dstStart)
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
const rowLen = copyW * 4
|
|
75
66
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
dstData[di + 3] = srcData[si + 3]
|
|
81
|
-
} else {
|
|
82
|
-
const a = alpha / 255
|
|
83
|
-
const invA = 1 - a
|
|
67
|
+
for (let row = 0; row < copyH; row++) {
|
|
68
|
+
const dstStart = ((dstY + row) * dstW + dstX) * 4
|
|
69
|
+
const srcStart = ((srcY + row) * srcW + srcX) * 4
|
|
70
|
+
const chunk = src.subarray(srcStart, srcStart + rowLen)
|
|
84
71
|
|
|
85
|
-
|
|
86
|
-
dstData[di + 1] = srcData[si + 1] * a + dstData[di + 1] * invA
|
|
87
|
-
dstData[di + 2] = srcData[si + 2] * a + dstData[di + 2] * invA
|
|
88
|
-
dstData[di + 3] = srcData[si + 3] * a + dstData[di + 3] * invA
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
const byteLen = copyW * 4
|
|
93
|
-
const sub = srcData.subarray(srcStart, srcStart + byteLen)
|
|
94
|
-
dstData.set(sub, dstStart)
|
|
72
|
+
dst.set(chunk, dstStart)
|
|
95
73
|
}
|
|
96
74
|
}
|
|
97
75
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import type { Rect } from '../Rect/_rect-types'
|
|
2
|
-
import { makeClippedBlit, resolveBlitClipping } from '../Rect/resolveClipping'
|
|
3
|
-
|
|
4
|
-
const SCRATCH_BLIT = makeClippedBlit()
|
|
5
2
|
|
|
6
3
|
/**
|
|
7
4
|
* Copies a pixel buffer into a specific region of an {@link ImageData} object.
|
|
@@ -43,43 +40,82 @@ export function writeImageDataBuffer(
|
|
|
43
40
|
_w?: number,
|
|
44
41
|
_h?: number,
|
|
45
42
|
): void {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
43
|
+
let x: number
|
|
44
|
+
let y: number
|
|
45
|
+
let w: number
|
|
46
|
+
let h: number
|
|
47
|
+
|
|
48
|
+
if (typeof _x === 'object') {
|
|
49
|
+
x = _x.x
|
|
50
|
+
y = _x.y
|
|
51
|
+
w = _x.w
|
|
52
|
+
h = _x.h
|
|
53
|
+
} else {
|
|
54
|
+
x = _x
|
|
55
|
+
y = _y!
|
|
56
|
+
w = _w!
|
|
57
|
+
h = _h!
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (w <= 0 || h <= 0) return
|
|
61
|
+
|
|
62
|
+
const dstW = target.width
|
|
63
|
+
const dstH = target.height
|
|
64
|
+
const dst = target.data
|
|
65
|
+
|
|
66
|
+
// Inline clipping logic for destination boundaries
|
|
67
|
+
let dstX = x
|
|
68
|
+
let dstY = y
|
|
69
|
+
let srcX = 0
|
|
70
|
+
let srcY = 0
|
|
71
|
+
let copyW = w
|
|
72
|
+
let copyH = h
|
|
73
|
+
|
|
74
|
+
if (dstX < 0) {
|
|
75
|
+
srcX = -dstX
|
|
76
|
+
copyW += dstX
|
|
77
|
+
dstX = 0
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (dstY < 0) {
|
|
81
|
+
srcY = -dstY
|
|
82
|
+
copyH += dstY
|
|
83
|
+
dstY = 0
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
copyW = Math.min(copyW, dstW - dstX)
|
|
87
|
+
copyH = Math.min(copyH, dstH - dstY)
|
|
88
|
+
|
|
89
|
+
if (copyW <= 0 || copyH <= 0) return
|
|
90
|
+
|
|
91
|
+
// Fast-path: Both arrays must be 4-byte aligned to use Uint32Array safely
|
|
92
|
+
const isDstAligned = dst.byteOffset % 4 === 0
|
|
93
|
+
const isSrcAligned = data.byteOffset % 4 === 0
|
|
94
|
+
|
|
95
|
+
if (isDstAligned && isSrcAligned) {
|
|
96
|
+
const dstLen32 = dst.byteLength / 4
|
|
97
|
+
const dst32 = new Uint32Array(dst.buffer, dst.byteOffset, dstLen32)
|
|
98
|
+
|
|
99
|
+
const srcLen32 = data.byteLength / 4
|
|
100
|
+
const src32 = new Uint32Array(data.buffer, data.byteOffset, srcLen32)
|
|
101
|
+
|
|
102
|
+
for (let row = 0; row < copyH; row++) {
|
|
103
|
+
const dstStart = (dstY + row) * dstW + dstX
|
|
104
|
+
const srcStart = (srcY + row) * w + srcX
|
|
105
|
+
const chunk = src32.subarray(srcStart, srcStart + copyW)
|
|
106
|
+
|
|
107
|
+
dst32.set(chunk, dstStart)
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
// Fallback for unaligned data arrays
|
|
111
|
+
const rowLen = copyW * 4
|
|
112
|
+
|
|
113
|
+
for (let row = 0; row < copyH; row++) {
|
|
114
|
+
const dstStart = ((dstY + row) * dstW + dstX) * 4
|
|
115
|
+
const srcStart = ((srcY + row) * w + srcX) * 4
|
|
116
|
+
const chunk = data.subarray(srcStart, srcStart + rowLen)
|
|
117
|
+
|
|
118
|
+
dst.set(chunk, dstStart)
|
|
119
|
+
}
|
|
84
120
|
}
|
|
85
121
|
}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import type { Rect } from '../Rect/_rect-types'
|
|
2
|
-
import { makeClippedBlit, resolveBlitClipping } from '../Rect/resolveClipping'
|
|
3
2
|
import type { PixelData32 } from './_pixelData-types'
|
|
4
3
|
|
|
5
|
-
const SCRATCH_BLIT = makeClippedBlit()
|
|
6
|
-
|
|
7
4
|
/**
|
|
8
5
|
* Extracts a rectangular region of pixels from PixelData.
|
|
9
6
|
* Returns a new Uint32Array containing the extracted pixels.
|
|
10
7
|
*/
|
|
11
|
-
export function extractPixelDataBuffer(
|
|
12
|
-
|
|
8
|
+
export function extractPixelDataBuffer(
|
|
9
|
+
source: PixelData32,
|
|
10
|
+
rect: Rect,
|
|
11
|
+
): Uint32Array
|
|
12
|
+
export function extractPixelDataBuffer(
|
|
13
|
+
source: PixelData32,
|
|
14
|
+
x: number,
|
|
15
|
+
y: number,
|
|
16
|
+
w: number,
|
|
17
|
+
h: number,
|
|
18
|
+
): Uint32Array
|
|
13
19
|
export function extractPixelDataBuffer(
|
|
14
20
|
source: PixelData32,
|
|
15
21
|
_x: Rect | number,
|
|
@@ -17,56 +23,61 @@ export function extractPixelDataBuffer(
|
|
|
17
23
|
_w?: number,
|
|
18
24
|
_h?: number,
|
|
19
25
|
): Uint32Array {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
let x: number
|
|
27
|
+
let y: number
|
|
28
|
+
let w: number
|
|
29
|
+
let h: number
|
|
30
|
+
|
|
31
|
+
if (typeof _x === 'object') {
|
|
32
|
+
x = _x.x
|
|
33
|
+
y = _x.y
|
|
34
|
+
w = _x.w
|
|
35
|
+
h = _x.h
|
|
36
|
+
} else {
|
|
37
|
+
x = _x
|
|
38
|
+
y = _y!
|
|
39
|
+
w = _w!
|
|
40
|
+
h = _h!
|
|
41
|
+
}
|
|
23
42
|
|
|
24
43
|
const srcW = source.w
|
|
25
44
|
const srcH = source.h
|
|
26
45
|
const srcData = source.data
|
|
27
46
|
|
|
28
|
-
|
|
29
|
-
if (w <= 0 || h <= 0) {
|
|
30
|
-
return new Uint32Array(0)
|
|
31
|
-
}
|
|
47
|
+
if (w <= 0 || h <= 0) return new Uint32Array(0)
|
|
32
48
|
|
|
33
49
|
const dstData = new Uint32Array(w * h)
|
|
34
50
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
srcW,
|
|
49
|
-
srcH,
|
|
50
|
-
SCRATCH_BLIT,
|
|
51
|
-
)
|
|
51
|
+
// Inline clipping logic to avoid object allocations
|
|
52
|
+
let srcX = x
|
|
53
|
+
let srcY = y
|
|
54
|
+
let dstX = 0
|
|
55
|
+
let dstY = 0
|
|
56
|
+
let copyW = w
|
|
57
|
+
let copyH = h
|
|
58
|
+
|
|
59
|
+
if (srcX < 0) {
|
|
60
|
+
dstX = -srcX
|
|
61
|
+
copyW += srcX
|
|
62
|
+
srcX = 0
|
|
63
|
+
}
|
|
52
64
|
|
|
53
|
-
if (
|
|
65
|
+
if (srcY < 0) {
|
|
66
|
+
dstY = -srcY
|
|
67
|
+
copyH += srcY
|
|
68
|
+
srcY = 0
|
|
69
|
+
}
|
|
54
70
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
sy: srcY,
|
|
60
|
-
w: copyW,
|
|
61
|
-
h: copyH,
|
|
62
|
-
} = clip
|
|
71
|
+
copyW = Math.min(copyW, srcW - srcX)
|
|
72
|
+
copyH = Math.min(copyH, srcH - srcY)
|
|
73
|
+
|
|
74
|
+
if (copyW <= 0 || copyH <= 0) return dstData
|
|
63
75
|
|
|
64
76
|
for (let row = 0; row < copyH; row++) {
|
|
65
77
|
const srcStart = (srcY + row) * srcW + srcX
|
|
66
78
|
const dstStart = (dstY + row) * w + dstX
|
|
67
|
-
|
|
68
|
-
// Perform the high-speed 32-bit bulk copy
|
|
69
79
|
const chunk = srcData.subarray(srcStart, srcStart + copyW)
|
|
80
|
+
|
|
70
81
|
dstData.set(chunk, dstStart)
|
|
71
82
|
}
|
|
72
83
|
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { Color32 } from '../_types'
|
|
2
2
|
import type { Rect } from '../Rect/_rect-types'
|
|
3
|
-
import { makeClippedRect, resolveRectClipping } from '../Rect/resolveClipping'
|
|
4
3
|
import type { PixelData32 } from './_pixelData-types'
|
|
5
4
|
|
|
6
|
-
const SCRATCH_RECT = makeClippedRect()
|
|
7
|
-
|
|
8
5
|
/**
|
|
9
6
|
* Fills a region or the {@link PixelData32} buffer with a solid color.
|
|
10
7
|
*
|
|
@@ -42,6 +39,9 @@ export function fillPixelData(
|
|
|
42
39
|
_w?: number,
|
|
43
40
|
_h?: number,
|
|
44
41
|
): boolean {
|
|
42
|
+
const dstW = dst.w
|
|
43
|
+
const dstH = dst.h
|
|
44
|
+
|
|
45
45
|
let x: number
|
|
46
46
|
let y: number
|
|
47
47
|
let w: number
|
|
@@ -50,8 +50,8 @@ export function fillPixelData(
|
|
|
50
50
|
if (typeof _x === 'object') {
|
|
51
51
|
x = _x.x ?? 0
|
|
52
52
|
y = _x.y ?? 0
|
|
53
|
-
w = _x.w ??
|
|
54
|
-
h = _x.h ??
|
|
53
|
+
w = _x.w ?? dstW
|
|
54
|
+
h = _x.h ?? dstH
|
|
55
55
|
} else if (typeof _x === 'number') {
|
|
56
56
|
x = _x
|
|
57
57
|
y = _y!
|
|
@@ -60,37 +60,54 @@ export function fillPixelData(
|
|
|
60
60
|
} else {
|
|
61
61
|
x = 0
|
|
62
62
|
y = 0
|
|
63
|
-
w =
|
|
64
|
-
h =
|
|
63
|
+
w = dstW
|
|
64
|
+
h = dstH
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Inline bounds clipping
|
|
68
|
+
let dstX = x
|
|
69
|
+
let dstY = y
|
|
70
|
+
let fillW = w
|
|
71
|
+
let fillH = h
|
|
72
|
+
|
|
73
|
+
if (dstX < 0) {
|
|
74
|
+
fillW += dstX
|
|
75
|
+
dstX = 0
|
|
65
76
|
}
|
|
66
77
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
h,
|
|
72
|
-
dst.w,
|
|
73
|
-
dst.h,
|
|
74
|
-
SCRATCH_RECT,
|
|
75
|
-
)
|
|
78
|
+
if (dstY < 0) {
|
|
79
|
+
fillH += dstY
|
|
80
|
+
dstY = 0
|
|
81
|
+
}
|
|
76
82
|
|
|
77
|
-
|
|
83
|
+
fillW = Math.min(fillW, dstW - dstX)
|
|
84
|
+
fillH = Math.min(fillH, dstH - dstY)
|
|
78
85
|
|
|
79
|
-
|
|
80
|
-
x: finalX,
|
|
81
|
-
y: finalY,
|
|
82
|
-
w: actualW,
|
|
83
|
-
h: actualH,
|
|
84
|
-
} = clip
|
|
86
|
+
if (fillW <= 0 || fillH <= 0) return false
|
|
85
87
|
|
|
86
88
|
const dst32 = dst.data
|
|
87
|
-
const dw = dst.w
|
|
88
89
|
let hasChanged = false
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const start =
|
|
93
|
-
const end = start +
|
|
91
|
+
// Fast-path: If the area spans the full width, we can treat it as a contiguous 1D array
|
|
92
|
+
if (dstX === 0 && fillW === dstW) {
|
|
93
|
+
const start = dstY * dstW
|
|
94
|
+
const end = start + fillW * fillH
|
|
95
|
+
|
|
96
|
+
for (let i = start; i < end; i++) {
|
|
97
|
+
if (dst32[i] !== color) {
|
|
98
|
+
dst32[i] = color
|
|
99
|
+
hasChanged = true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return hasChanged
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Standard path: row-by-row
|
|
107
|
+
for (let iy = 0; iy < fillH; iy++) {
|
|
108
|
+
const rowOffset = (dstY + iy) * dstW
|
|
109
|
+
const start = rowOffset + dstX
|
|
110
|
+
const end = start + fillW
|
|
94
111
|
|
|
95
112
|
for (let i = start; i < end; i++) {
|
|
96
113
|
if (dst32[i] !== color) {
|