pixel-data-js 0.10.0 → 0.11.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.dev.cjs +50 -59
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +50 -59
- package/dist/index.dev.js.map +1 -1
- package/dist/index.prod.cjs +50 -59
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +11 -3
- package/dist/index.prod.js +50 -59
- package/dist/index.prod.js.map +1 -1
- package/package.json +1 -1
- package/src/ImageData/resampleImageData.ts +10 -28
- package/src/IndexedImage/IndexedImage.ts +23 -14
- package/src/IndexedImage/resampleIndexedImage.ts +14 -19
- package/src/Internal/resample32.ts +40 -0
- package/src/PixelData/resamplePixelData.ts +14 -20
package/package.json
CHANGED
|
@@ -2,34 +2,16 @@
|
|
|
2
2
|
* Resamples ImageData by a specific factor.
|
|
3
3
|
* Factor > 1 upscales, Factor < 1 downscales.
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
source: ImageData,
|
|
7
|
-
factor: number,
|
|
8
|
-
): ImageData {
|
|
9
|
-
const srcW = source.width
|
|
10
|
-
const srcH = source.height
|
|
11
|
-
const dstW = Math.max(1, (srcW * factor) | 0)
|
|
12
|
-
const dstH = Math.max(1, (srcH * factor) | 0)
|
|
13
|
-
const srcData = source.data
|
|
14
|
-
const dstData = new Uint8ClampedArray(dstW * dstH * 4)
|
|
5
|
+
import { resample32 } from '../Internal/resample32'
|
|
15
6
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const srcIdx = srcRowOffset + srcX * 4
|
|
24
|
-
const dstIdx = dstRowOffset + x * 4
|
|
25
|
-
|
|
26
|
-
// Copy RGBA channels
|
|
27
|
-
dstData[dstIdx] = srcData[srcIdx]!
|
|
28
|
-
dstData[dstIdx + 1] = srcData[srcIdx + 1]!
|
|
29
|
-
dstData[dstIdx + 2] = srcData[srcIdx + 2]!
|
|
30
|
-
dstData[dstIdx + 3] = srcData[srcIdx + 3]!
|
|
31
|
-
}
|
|
32
|
-
}
|
|
7
|
+
/**
|
|
8
|
+
* Resamples ImageData by a specific factor.
|
|
9
|
+
* Factor > 1 upscales, Factor < 1 downscales.
|
|
10
|
+
*/
|
|
11
|
+
export function resampleImageData(source: ImageData, factor: number): ImageData {
|
|
12
|
+
const src32 = new Uint32Array(source.data.buffer)
|
|
13
|
+
const { data, width, height } = resample32(src32, source.width, source.height, factor)
|
|
33
14
|
|
|
34
|
-
|
|
15
|
+
const uint8ClampedArray = new Uint8ClampedArray(data.buffer) as Uint8ClampedArray<ArrayBuffer>
|
|
16
|
+
return new ImageData(uint8ClampedArray, width, height)
|
|
35
17
|
}
|
|
@@ -26,15 +26,24 @@ export type IndexedImage = {
|
|
|
26
26
|
/**
|
|
27
27
|
* Converts standard ImageData into an IndexedImage format.
|
|
28
28
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
export function makeIndexedImage(imageData: ImageData): IndexedImage;
|
|
30
|
+
export function makeIndexedImage(
|
|
31
|
+
data: Uint8ClampedArray,
|
|
32
|
+
width: number,
|
|
33
|
+
height: number,
|
|
34
|
+
): IndexedImage;
|
|
35
|
+
export function makeIndexedImage(
|
|
36
|
+
imageOrData: ImageData | Uint8ClampedArray,
|
|
37
|
+
width?: number,
|
|
38
|
+
height?: number,
|
|
39
|
+
): IndexedImage {
|
|
40
|
+
const isImageData = 'width' in imageOrData
|
|
41
|
+
const actualWidth = isImageData ? imageOrData.width : (width as number)
|
|
42
|
+
const actualHeight = isImageData ? imageOrData.height : (height as number)
|
|
43
|
+
const buffer = isImageData ? imageOrData.data.buffer : imageOrData.buffer
|
|
35
44
|
|
|
36
|
-
// Use a 32-bit view to read pixels as packed integers (
|
|
37
|
-
const rawData = new Uint32Array(
|
|
45
|
+
// Use a 32-bit view to read pixels as packed integers (unsigned)
|
|
46
|
+
const rawData = new Uint32Array(buffer)
|
|
38
47
|
const indexedData = new Int32Array(rawData.length)
|
|
39
48
|
const colorMap = new Map<number, number>()
|
|
40
49
|
|
|
@@ -47,15 +56,14 @@ export function makeIndexedImage(imageData: ImageData): IndexedImage {
|
|
|
47
56
|
for (let i = 0; i < rawData.length; i++) {
|
|
48
57
|
const pixel = rawData[i]!
|
|
49
58
|
|
|
50
|
-
// Check if the pixel is fully transparent
|
|
59
|
+
// Check if the pixel is fully transparent (Alpha channel is highest byte)
|
|
51
60
|
const alpha = (pixel >>> 24) & 0xFF
|
|
52
61
|
const isTransparent = alpha === 0
|
|
53
|
-
const colorKey = isTransparent ? transparentColor : pixel
|
|
62
|
+
const colorKey = isTransparent ? transparentColor : (pixel >>> 0)
|
|
54
63
|
|
|
55
64
|
let id = colorMap.get(colorKey)
|
|
56
65
|
|
|
57
66
|
if (id === undefined) {
|
|
58
|
-
// Use the current length as the next ID to ensure sequence
|
|
59
67
|
id = colorMap.size
|
|
60
68
|
colorMap.set(colorKey, id)
|
|
61
69
|
}
|
|
@@ -63,10 +71,11 @@ export function makeIndexedImage(imageData: ImageData): IndexedImage {
|
|
|
63
71
|
indexedData[i] = id
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
const palette =
|
|
74
|
+
const palette = Uint32Array.from(colorMap.keys())
|
|
75
|
+
|
|
67
76
|
return {
|
|
68
|
-
width,
|
|
69
|
-
height,
|
|
77
|
+
width: actualWidth,
|
|
78
|
+
height: actualHeight,
|
|
70
79
|
data: indexedData,
|
|
71
80
|
transparentPalletIndex,
|
|
72
81
|
palette,
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resamples an IndexedImage by a specific factor using nearest neighbor
|
|
3
|
+
* Factor > 1 upscales, Factor < 1 downscales.
|
|
4
|
+
*/
|
|
1
5
|
import type { IndexedImage } from '../index'
|
|
6
|
+
import { resample32 } from '../Internal/resample32'
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* Resamples an IndexedImage by a specific factor using nearest neighbor
|
|
@@ -8,28 +13,18 @@ export function resampleIndexedImage(
|
|
|
8
13
|
source: IndexedImage,
|
|
9
14
|
factor: number,
|
|
10
15
|
): IndexedImage {
|
|
11
|
-
const srcW = source.width
|
|
12
|
-
const srcH = source.height
|
|
13
|
-
const dstW = srcW * factor
|
|
14
|
-
const dstH = srcH * factor
|
|
15
|
-
const srcData = source.data
|
|
16
|
-
const dstData = new Int32Array(dstW * dstH)
|
|
17
|
-
|
|
18
|
-
for (let y = 0; y < dstH; y++) {
|
|
19
|
-
const srcY = (y / factor) | 0
|
|
20
|
-
const rowOffset = srcY * srcW
|
|
21
|
-
const dstOffset = y * dstW
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
const { data, width, height } = resample32(
|
|
18
|
+
source.data,
|
|
19
|
+
source.width,
|
|
20
|
+
source.height,
|
|
21
|
+
factor,
|
|
22
|
+
)
|
|
28
23
|
|
|
29
24
|
return {
|
|
30
|
-
width
|
|
31
|
-
height
|
|
32
|
-
data
|
|
25
|
+
width,
|
|
26
|
+
height,
|
|
27
|
+
data,
|
|
33
28
|
palette: source.palette,
|
|
34
29
|
transparentPalletIndex: source.transparentPalletIndex,
|
|
35
30
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const resample32Scratch = {
|
|
2
|
+
data: null as null | Int32Array,
|
|
3
|
+
width: 0,
|
|
4
|
+
height: 0,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
type Resample32Result = { data: Int32Array; width: number; height: number }
|
|
8
|
+
|
|
9
|
+
export function resample32(
|
|
10
|
+
srcData32: Uint32Array | Int32Array,
|
|
11
|
+
srcW: number,
|
|
12
|
+
srcH: number,
|
|
13
|
+
factor: number,
|
|
14
|
+
): Resample32Result {
|
|
15
|
+
const dstW = Math.max(1, (srcW * factor) | 0)
|
|
16
|
+
const dstH = Math.max(1, (srcH * factor) | 0)
|
|
17
|
+
const dstData = new Int32Array(dstW * dstH)
|
|
18
|
+
|
|
19
|
+
// Use the reciprocal to map back precisely
|
|
20
|
+
const scaleX = srcW / dstW
|
|
21
|
+
const scaleY = srcH / dstH
|
|
22
|
+
|
|
23
|
+
for (let y = 0; y < dstH; y++) {
|
|
24
|
+
const srcY = Math.min(srcH - 1, (y * scaleY) | 0)
|
|
25
|
+
const srcRowOffset = srcY * srcW
|
|
26
|
+
const dstRowOffset = y * dstW
|
|
27
|
+
|
|
28
|
+
for (let x = 0; x < dstW; x++) {
|
|
29
|
+
const srcX = Math.min(srcW - 1, (x * scaleX) | 0)
|
|
30
|
+
|
|
31
|
+
dstData[dstRowOffset + x] = srcData32[srcRowOffset + srcX]!
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
resample32Scratch.data = dstData
|
|
36
|
+
resample32Scratch.width = dstW
|
|
37
|
+
resample32Scratch.height = dstH
|
|
38
|
+
|
|
39
|
+
return resample32Scratch as Resample32Result
|
|
40
|
+
}
|
|
@@ -1,29 +1,23 @@
|
|
|
1
|
-
import { PixelData } from '../index'
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Resamples an PixelData by a specific factor using nearest neighbor
|
|
5
3
|
* Factor > 1 upscales, Factor < 1 downscales.
|
|
6
4
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const dstH = Math.max(1, (pixelData.height * factor) | 0)
|
|
10
|
-
const dstBuffer = new Uint8ClampedArray(dstW * dstH * 4)
|
|
11
|
-
const dstData32 = new Uint32Array(dstBuffer.buffer)
|
|
12
|
-
|
|
13
|
-
for (let y = 0; y < dstH; y++) {
|
|
14
|
-
const srcY = (y / factor) | 0
|
|
15
|
-
const srcRowOffset = srcY * pixelData.width
|
|
16
|
-
const dstRowOffset = y * dstW
|
|
5
|
+
import { PixelData } from '../index'
|
|
6
|
+
import { resample32 } from '../Internal/resample32'
|
|
17
7
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Resamples PixelData by a specific factor using nearest neighbor.
|
|
10
|
+
* Factor > 1 upscales, Factor < 1 downscales.
|
|
11
|
+
*/
|
|
12
|
+
export function resamplePixelData(
|
|
13
|
+
pixelData: PixelData,
|
|
14
|
+
factor: number,
|
|
15
|
+
): PixelData {
|
|
16
|
+
const { data, width, height } = resample32(pixelData.data32, pixelData.width, pixelData.height, factor)
|
|
23
17
|
|
|
24
18
|
return new PixelData({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
width,
|
|
20
|
+
height,
|
|
21
|
+
data: new Uint8ClampedArray(data.buffer),
|
|
28
22
|
})
|
|
29
23
|
}
|