pixel-data-js 0.0.2 → 0.0.3
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 +38 -2
- package/dist/index.dev.cjs +28 -9
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +25 -9
- package/dist/index.dev.js.map +1 -1
- package/dist/index.prod.cjs +28 -9
- package/dist/index.prod.cjs.map +1 -1
- package/dist/index.prod.d.ts +16 -4
- package/dist/index.prod.js +25 -9
- package/dist/index.prod.js.map +1 -1
- package/package.json +1 -1
- package/src/ImageData/read-write-pixels.ts +36 -0
- package/src/ImageData/serialization.ts +28 -17
- package/src/_types.ts +22 -0
- package/src/color.ts +79 -0
package/README.md
CHANGED
|
@@ -1,24 +1,60 @@
|
|
|
1
1
|
# Pixel Data JS
|
|
2
2
|
|
|
3
|
-
A library of functions for interacting with pixel data and ImageData objects.
|
|
3
|
+
A **🔥Performance🔥** library of functions for interacting with pixel data and ImageData objects.
|
|
4
4
|
This package is designed to be a tree-shake friendly list of functions.
|
|
5
5
|
|
|
6
|
+
[Documentation](https://unstoppablecarl.github.io/pixel-data-js/)
|
|
7
|
+
|
|
6
8
|
## Installation
|
|
7
9
|
|
|
8
10
|
`$ npm i pixel-data-js`
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## High-Performance Pixel Manipulation with `Uint32Array`
|
|
14
|
+
|
|
15
|
+
The `ImageData.data` object is a `Uint8ClampedArray`.
|
|
16
|
+
It is easy to use but, it is inefficient for heavy processing because every single pixel requires four separate write operations (Red, Green, Blue, and Alpha).
|
|
17
|
+
By using a `Uint32Array` view, we can treat all four color channels as a single 32-bit integer, allowing us to update an entire pixel in a single CPU operation.
|
|
18
|
+
|
|
19
|
+
## The Concept: 32-bit Packing
|
|
20
|
+
A single pixel consists of four 8-bit channels (R, G, B, A). The entire color's data can fit into a single 32-bit unsigned integer `8 * 4 = 32` (`Color32`).
|
|
21
|
+
|
|
22
|
+
## Example
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { packColor } from 'pixel-data-js'
|
|
26
|
+
|
|
27
|
+
const ctx = canvas.getContext('2d')
|
|
28
|
+
const imageData = ctx.getImageData(0, 0, width, height)
|
|
29
|
+
|
|
30
|
+
// 1. Get the underlying buffer
|
|
31
|
+
const buffer = imageData.data.buffer
|
|
32
|
+
|
|
33
|
+
// 2. Create a 32-bit view of that same buffer
|
|
34
|
+
const data32 = new Uint32Array(buffer)
|
|
35
|
+
|
|
36
|
+
// 3. Write a single pixel (Red: 255, Green: 100, Blue: 0, Alpha: 255)
|
|
37
|
+
// This is 4x faster than writing to imageData.data[i...i+3]
|
|
38
|
+
data32[0] = packColor(255, 100, 0, 255)
|
|
39
|
+
|
|
40
|
+
// 4. Push back to canvas
|
|
41
|
+
ctx.putImageData(imageData, 0, 0)
|
|
42
|
+
```
|
|
43
|
+
### Color Integers
|
|
44
|
+
|
|
45
|
+
You can define colors using the `AABBGGRR` (Little-Endian) to make a `Color32` object.
|
|
12
46
|
|
|
13
47
|
|
|
14
48
|
## Building
|
|
15
49
|
|
|
16
50
|
`$ pnpm install`
|
|
51
|
+
|
|
17
52
|
`$ pnpm run build`
|
|
18
53
|
|
|
19
54
|
## Testing
|
|
20
55
|
|
|
21
56
|
`$ pnpm run test`
|
|
57
|
+
|
|
22
58
|
`$ pnpm run test:mutation`
|
|
23
59
|
|
|
24
60
|
## Releases Automation
|
package/dist/index.dev.cjs
CHANGED
|
@@ -20,34 +20,50 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
|
|
24
|
+
base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
|
|
23
25
|
deserializeImageData: () => deserializeImageData,
|
|
24
26
|
deserializeNullableImageData: () => deserializeNullableImageData,
|
|
27
|
+
deserializeRawImageData: () => deserializeRawImageData,
|
|
25
28
|
serializeImageData: () => serializeImageData,
|
|
26
29
|
serializeNullableImageData: () => serializeNullableImageData
|
|
27
30
|
});
|
|
28
31
|
module.exports = __toCommonJS(src_exports);
|
|
29
32
|
|
|
30
33
|
// src/ImageData/serialization.ts
|
|
34
|
+
function base64EncodeArrayBuffer(buffer) {
|
|
35
|
+
const binary = String.fromCharCode(...new Uint8Array(buffer));
|
|
36
|
+
return btoa(binary);
|
|
37
|
+
}
|
|
38
|
+
function base64DecodeArrayBuffer(encoded) {
|
|
39
|
+
const binary = atob(encoded);
|
|
40
|
+
const bytes = new Uint8ClampedArray(binary.length);
|
|
41
|
+
for (let i = 0; i < binary.length; i++) {
|
|
42
|
+
bytes[i] = binary.charCodeAt(i);
|
|
43
|
+
}
|
|
44
|
+
return bytes;
|
|
45
|
+
}
|
|
31
46
|
function serializeImageData(imageData) {
|
|
32
|
-
const binary = String.fromCharCode(...new Uint8Array(imageData.data.buffer));
|
|
33
|
-
const base64 = btoa(binary);
|
|
34
47
|
return {
|
|
35
48
|
width: imageData.width,
|
|
36
49
|
height: imageData.height,
|
|
37
|
-
data:
|
|
50
|
+
data: base64EncodeArrayBuffer(imageData.data.buffer)
|
|
38
51
|
};
|
|
39
52
|
}
|
|
40
53
|
function serializeNullableImageData(imageData) {
|
|
41
54
|
if (!imageData) return null;
|
|
42
55
|
return serializeImageData(imageData);
|
|
43
56
|
}
|
|
57
|
+
function deserializeRawImageData(serialized) {
|
|
58
|
+
return {
|
|
59
|
+
width: serialized.width,
|
|
60
|
+
height: serialized.height,
|
|
61
|
+
data: base64DecodeArrayBuffer(serialized.data)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
44
64
|
function deserializeImageData(serialized) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
for (let i = 0; i < binary.length; i++) {
|
|
48
|
-
bytes[i] = binary.charCodeAt(i);
|
|
49
|
-
}
|
|
50
|
-
return new ImageData(bytes, serialized.width, serialized.height);
|
|
65
|
+
const data = base64DecodeArrayBuffer(serialized.data);
|
|
66
|
+
return new ImageData(data, serialized.width, serialized.height);
|
|
51
67
|
}
|
|
52
68
|
function deserializeNullableImageData(serialized) {
|
|
53
69
|
if (!serialized) return null;
|
|
@@ -55,8 +71,11 @@ function deserializeNullableImageData(serialized) {
|
|
|
55
71
|
}
|
|
56
72
|
// Annotate the CommonJS export names for ESM import in node:
|
|
57
73
|
0 && (module.exports = {
|
|
74
|
+
base64DecodeArrayBuffer,
|
|
75
|
+
base64EncodeArrayBuffer,
|
|
58
76
|
deserializeImageData,
|
|
59
77
|
deserializeNullableImageData,
|
|
78
|
+
deserializeRawImageData,
|
|
60
79
|
serializeImageData,
|
|
61
80
|
serializeNullableImageData
|
|
62
81
|
});
|
package/dist/index.dev.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/ImageData/serialization.ts"],"sourcesContent":["export * from './ImageData/serialization'\n","
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ImageData/serialization.ts"],"sourcesContent":["export * from './ImageData/serialization'\n","import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'\n\nexport function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {\n const binary = String.fromCharCode(...new Uint8Array(buffer))\n return btoa(binary) as Base64EncodedUInt8Array\n}\n\nexport function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray {\n const binary = atob(encoded)\n const bytes = new Uint8ClampedArray(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n/**\n * Serialize for use in JSON. Pixel data is stored as base64 encoded string.\n */\nexport function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData {\n return {\n width: imageData.width,\n height: imageData.height,\n data: base64EncodeArrayBuffer(imageData.data.buffer),\n }\n}\n\nexport function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData {\n if (!imageData) return null as any\n\n return serializeImageData(imageData) as any\n}\n\nexport function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike {\n return {\n width: serialized.width,\n height: serialized.height,\n data: base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array),\n }\n}\n\nexport function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData {\n const data = base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array)\n\n return new ImageData(data as ImageDataArray, serialized.width, serialized.height) as any\n}\n\nexport function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData {\n if (!serialized) return null as any\n return deserializeImageData(serialized) as any\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,SAAS,wBAAwB,QAAkD;AACxF,QAAM,SAAS,OAAO,aAAa,GAAG,IAAI,WAAW,MAAM,CAAC;AAC5D,SAAO,KAAK,MAAM;AACpB;AAEO,SAAS,wBAAwB,SAAqD;AAC3F,QAAM,SAAS,KAAK,OAAO;AAC3B,QAAM,QAAQ,IAAI,kBAAkB,OAAO,MAAM;AACjD,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAKO,SAAS,mBAA4C,WAAmC;AAC7F,SAAO;AAAA,IACL,OAAO,UAAU;AAAA,IACjB,QAAQ,UAAU;AAAA,IAClB,MAAM,wBAAwB,UAAU,KAAK,MAAM;AAAA,EACrD;AACF;AAEO,SAAS,2BAA2D,WAA2D;AACpI,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO,mBAAmB,SAAS;AACrC;AAEO,SAAS,wBAAuD,YAA8B;AACnG,SAAO;AAAA,IACL,OAAO,WAAW;AAAA,IAClB,QAAQ,WAAW;AAAA,IACnB,MAAM,wBAAwB,WAAW,IAA+B;AAAA,EAC1E;AACF;AAEO,SAAS,qBAAoD,YAA0B;AAC5F,QAAM,OAAO,wBAAwB,WAAW,IAA+B;AAE/E,SAAO,IAAI,UAAU,MAAwB,WAAW,OAAO,WAAW,MAAM;AAClF;AAEO,SAAS,6BAAmE,YAAkD;AACnI,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,qBAAqB,UAAU;AACxC;","names":[]}
|
package/dist/index.dev.js
CHANGED
|
@@ -1,32 +1,48 @@
|
|
|
1
1
|
// src/ImageData/serialization.ts
|
|
2
|
+
function base64EncodeArrayBuffer(buffer) {
|
|
3
|
+
const binary = String.fromCharCode(...new Uint8Array(buffer));
|
|
4
|
+
return btoa(binary);
|
|
5
|
+
}
|
|
6
|
+
function base64DecodeArrayBuffer(encoded) {
|
|
7
|
+
const binary = atob(encoded);
|
|
8
|
+
const bytes = new Uint8ClampedArray(binary.length);
|
|
9
|
+
for (let i = 0; i < binary.length; i++) {
|
|
10
|
+
bytes[i] = binary.charCodeAt(i);
|
|
11
|
+
}
|
|
12
|
+
return bytes;
|
|
13
|
+
}
|
|
2
14
|
function serializeImageData(imageData) {
|
|
3
|
-
const binary = String.fromCharCode(...new Uint8Array(imageData.data.buffer));
|
|
4
|
-
const base64 = btoa(binary);
|
|
5
15
|
return {
|
|
6
16
|
width: imageData.width,
|
|
7
17
|
height: imageData.height,
|
|
8
|
-
data:
|
|
18
|
+
data: base64EncodeArrayBuffer(imageData.data.buffer)
|
|
9
19
|
};
|
|
10
20
|
}
|
|
11
21
|
function serializeNullableImageData(imageData) {
|
|
12
22
|
if (!imageData) return null;
|
|
13
23
|
return serializeImageData(imageData);
|
|
14
24
|
}
|
|
25
|
+
function deserializeRawImageData(serialized) {
|
|
26
|
+
return {
|
|
27
|
+
width: serialized.width,
|
|
28
|
+
height: serialized.height,
|
|
29
|
+
data: base64DecodeArrayBuffer(serialized.data)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
15
32
|
function deserializeImageData(serialized) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < binary.length; i++) {
|
|
19
|
-
bytes[i] = binary.charCodeAt(i);
|
|
20
|
-
}
|
|
21
|
-
return new ImageData(bytes, serialized.width, serialized.height);
|
|
33
|
+
const data = base64DecodeArrayBuffer(serialized.data);
|
|
34
|
+
return new ImageData(data, serialized.width, serialized.height);
|
|
22
35
|
}
|
|
23
36
|
function deserializeNullableImageData(serialized) {
|
|
24
37
|
if (!serialized) return null;
|
|
25
38
|
return deserializeImageData(serialized);
|
|
26
39
|
}
|
|
27
40
|
export {
|
|
41
|
+
base64DecodeArrayBuffer,
|
|
42
|
+
base64EncodeArrayBuffer,
|
|
28
43
|
deserializeImageData,
|
|
29
44
|
deserializeNullableImageData,
|
|
45
|
+
deserializeRawImageData,
|
|
30
46
|
serializeImageData,
|
|
31
47
|
serializeNullableImageData
|
|
32
48
|
};
|
package/dist/index.dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ImageData/serialization.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../src/ImageData/serialization.ts"],"sourcesContent":["import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'\n\nexport function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {\n const binary = String.fromCharCode(...new Uint8Array(buffer))\n return btoa(binary) as Base64EncodedUInt8Array\n}\n\nexport function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray {\n const binary = atob(encoded)\n const bytes = new Uint8ClampedArray(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n/**\n * Serialize for use in JSON. Pixel data is stored as base64 encoded string.\n */\nexport function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData {\n return {\n width: imageData.width,\n height: imageData.height,\n data: base64EncodeArrayBuffer(imageData.data.buffer),\n }\n}\n\nexport function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData {\n if (!imageData) return null as any\n\n return serializeImageData(imageData) as any\n}\n\nexport function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike {\n return {\n width: serialized.width,\n height: serialized.height,\n data: base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array),\n }\n}\n\nexport function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData {\n const data = base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array)\n\n return new ImageData(data as ImageDataArray, serialized.width, serialized.height) as any\n}\n\nexport function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData {\n if (!serialized) return null as any\n return deserializeImageData(serialized) as any\n}\n"],"mappings":";AAEO,SAAS,wBAAwB,QAAkD;AACxF,QAAM,SAAS,OAAO,aAAa,GAAG,IAAI,WAAW,MAAM,CAAC;AAC5D,SAAO,KAAK,MAAM;AACpB;AAEO,SAAS,wBAAwB,SAAqD;AAC3F,QAAM,SAAS,KAAK,OAAO;AAC3B,QAAM,QAAQ,IAAI,kBAAkB,OAAO,MAAM;AACjD,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAKO,SAAS,mBAA4C,WAAmC;AAC7F,SAAO;AAAA,IACL,OAAO,UAAU;AAAA,IACjB,QAAQ,UAAU;AAAA,IAClB,MAAM,wBAAwB,UAAU,KAAK,MAAM;AAAA,EACrD;AACF;AAEO,SAAS,2BAA2D,WAA2D;AACpI,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO,mBAAmB,SAAS;AACrC;AAEO,SAAS,wBAAuD,YAA8B;AACnG,SAAO;AAAA,IACL,OAAO,WAAW;AAAA,IAClB,QAAQ,WAAW;AAAA,IACnB,MAAM,wBAAwB,WAAW,IAA+B;AAAA,EAC1E;AACF;AAEO,SAAS,qBAAoD,YAA0B;AAC5F,QAAM,OAAO,wBAAwB,WAAW,IAA+B;AAE/E,SAAO,IAAI,UAAU,MAAwB,WAAW,OAAO,WAAW,MAAM;AAClF;AAEO,SAAS,6BAAmE,YAAkD;AACnI,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,qBAAqB,UAAU;AACxC;","names":[]}
|
package/dist/index.prod.cjs
CHANGED
|
@@ -20,34 +20,50 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
base64DecodeArrayBuffer: () => base64DecodeArrayBuffer,
|
|
24
|
+
base64EncodeArrayBuffer: () => base64EncodeArrayBuffer,
|
|
23
25
|
deserializeImageData: () => deserializeImageData,
|
|
24
26
|
deserializeNullableImageData: () => deserializeNullableImageData,
|
|
27
|
+
deserializeRawImageData: () => deserializeRawImageData,
|
|
25
28
|
serializeImageData: () => serializeImageData,
|
|
26
29
|
serializeNullableImageData: () => serializeNullableImageData
|
|
27
30
|
});
|
|
28
31
|
module.exports = __toCommonJS(src_exports);
|
|
29
32
|
|
|
30
33
|
// src/ImageData/serialization.ts
|
|
34
|
+
function base64EncodeArrayBuffer(buffer) {
|
|
35
|
+
const binary = String.fromCharCode(...new Uint8Array(buffer));
|
|
36
|
+
return btoa(binary);
|
|
37
|
+
}
|
|
38
|
+
function base64DecodeArrayBuffer(encoded) {
|
|
39
|
+
const binary = atob(encoded);
|
|
40
|
+
const bytes = new Uint8ClampedArray(binary.length);
|
|
41
|
+
for (let i = 0; i < binary.length; i++) {
|
|
42
|
+
bytes[i] = binary.charCodeAt(i);
|
|
43
|
+
}
|
|
44
|
+
return bytes;
|
|
45
|
+
}
|
|
31
46
|
function serializeImageData(imageData) {
|
|
32
|
-
const binary = String.fromCharCode(...new Uint8Array(imageData.data.buffer));
|
|
33
|
-
const base64 = btoa(binary);
|
|
34
47
|
return {
|
|
35
48
|
width: imageData.width,
|
|
36
49
|
height: imageData.height,
|
|
37
|
-
data:
|
|
50
|
+
data: base64EncodeArrayBuffer(imageData.data.buffer)
|
|
38
51
|
};
|
|
39
52
|
}
|
|
40
53
|
function serializeNullableImageData(imageData) {
|
|
41
54
|
if (!imageData) return null;
|
|
42
55
|
return serializeImageData(imageData);
|
|
43
56
|
}
|
|
57
|
+
function deserializeRawImageData(serialized) {
|
|
58
|
+
return {
|
|
59
|
+
width: serialized.width,
|
|
60
|
+
height: serialized.height,
|
|
61
|
+
data: base64DecodeArrayBuffer(serialized.data)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
44
64
|
function deserializeImageData(serialized) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
for (let i = 0; i < binary.length; i++) {
|
|
48
|
-
bytes[i] = binary.charCodeAt(i);
|
|
49
|
-
}
|
|
50
|
-
return new ImageData(bytes, serialized.width, serialized.height);
|
|
65
|
+
const data = base64DecodeArrayBuffer(serialized.data);
|
|
66
|
+
return new ImageData(data, serialized.width, serialized.height);
|
|
51
67
|
}
|
|
52
68
|
function deserializeNullableImageData(serialized) {
|
|
53
69
|
if (!serialized) return null;
|
|
@@ -55,8 +71,11 @@ function deserializeNullableImageData(serialized) {
|
|
|
55
71
|
}
|
|
56
72
|
// Annotate the CommonJS export names for ESM import in node:
|
|
57
73
|
0 && (module.exports = {
|
|
74
|
+
base64DecodeArrayBuffer,
|
|
75
|
+
base64EncodeArrayBuffer,
|
|
58
76
|
deserializeImageData,
|
|
59
77
|
deserializeNullableImageData,
|
|
78
|
+
deserializeRawImageData,
|
|
60
79
|
serializeImageData,
|
|
61
80
|
serializeNullableImageData
|
|
62
81
|
});
|
package/dist/index.prod.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/ImageData/serialization.ts"],"sourcesContent":["export * from './ImageData/serialization'\n","
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ImageData/serialization.ts"],"sourcesContent":["export * from './ImageData/serialization'\n","import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'\n\nexport function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {\n const binary = String.fromCharCode(...new Uint8Array(buffer))\n return btoa(binary) as Base64EncodedUInt8Array\n}\n\nexport function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray {\n const binary = atob(encoded)\n const bytes = new Uint8ClampedArray(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n/**\n * Serialize for use in JSON. Pixel data is stored as base64 encoded string.\n */\nexport function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData {\n return {\n width: imageData.width,\n height: imageData.height,\n data: base64EncodeArrayBuffer(imageData.data.buffer),\n }\n}\n\nexport function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData {\n if (!imageData) return null as any\n\n return serializeImageData(imageData) as any\n}\n\nexport function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike {\n return {\n width: serialized.width,\n height: serialized.height,\n data: base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array),\n }\n}\n\nexport function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData {\n const data = base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array)\n\n return new ImageData(data as ImageDataArray, serialized.width, serialized.height) as any\n}\n\nexport function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData {\n if (!serialized) return null as any\n return deserializeImageData(serialized) as any\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,SAAS,wBAAwB,QAAkD;AACxF,QAAM,SAAS,OAAO,aAAa,GAAG,IAAI,WAAW,MAAM,CAAC;AAC5D,SAAO,KAAK,MAAM;AACpB;AAEO,SAAS,wBAAwB,SAAqD;AAC3F,QAAM,SAAS,KAAK,OAAO;AAC3B,QAAM,QAAQ,IAAI,kBAAkB,OAAO,MAAM;AACjD,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAKO,SAAS,mBAA4C,WAAmC;AAC7F,SAAO;AAAA,IACL,OAAO,UAAU;AAAA,IACjB,QAAQ,UAAU;AAAA,IAClB,MAAM,wBAAwB,UAAU,KAAK,MAAM;AAAA,EACrD;AACF;AAEO,SAAS,2BAA2D,WAA2D;AACpI,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO,mBAAmB,SAAS;AACrC;AAEO,SAAS,wBAAuD,YAA8B;AACnG,SAAO;AAAA,IACL,OAAO,WAAW;AAAA,IAClB,QAAQ,WAAW;AAAA,IACnB,MAAM,wBAAwB,WAAW,IAA+B;AAAA,EAC1E;AACF;AAEO,SAAS,qBAAoD,YAA0B;AAC5F,QAAM,OAAO,wBAAwB,WAAW,IAA+B;AAE/E,SAAO,IAAI,UAAU,MAAwB,WAAW,OAAO,WAAW,MAAM;AAClF;AAEO,SAAS,6BAAmE,YAAkD;AACnI,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,qBAAqB,UAAU;AACxC;","names":[]}
|
package/dist/index.prod.d.ts
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
|
+
type ImageDataLike = {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
data: Uint8ClampedArray;
|
|
5
|
+
};
|
|
1
6
|
type SerializedImageData = {
|
|
2
7
|
width: number;
|
|
3
8
|
height: number;
|
|
4
9
|
data: string;
|
|
5
10
|
};
|
|
11
|
+
type Base64EncodedUInt8Array = string & {
|
|
12
|
+
readonly __brandBase64UInt8Array: unique symbol;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
declare function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array;
|
|
16
|
+
declare function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray;
|
|
6
17
|
/**
|
|
7
|
-
* Serialize for use in JSON.
|
|
18
|
+
* Serialize for use in JSON. Pixel data is stored as base64 encoded string.
|
|
8
19
|
*/
|
|
9
|
-
declare function serializeImageData<T extends
|
|
10
|
-
declare function serializeNullableImageData<T extends
|
|
20
|
+
declare function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData;
|
|
21
|
+
declare function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData;
|
|
22
|
+
declare function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike;
|
|
11
23
|
declare function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData;
|
|
12
24
|
declare function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData;
|
|
13
25
|
|
|
14
|
-
export {
|
|
26
|
+
export { base64DecodeArrayBuffer, base64EncodeArrayBuffer, deserializeImageData, deserializeNullableImageData, deserializeRawImageData, serializeImageData, serializeNullableImageData };
|
package/dist/index.prod.js
CHANGED
|
@@ -1,32 +1,48 @@
|
|
|
1
1
|
// src/ImageData/serialization.ts
|
|
2
|
+
function base64EncodeArrayBuffer(buffer) {
|
|
3
|
+
const binary = String.fromCharCode(...new Uint8Array(buffer));
|
|
4
|
+
return btoa(binary);
|
|
5
|
+
}
|
|
6
|
+
function base64DecodeArrayBuffer(encoded) {
|
|
7
|
+
const binary = atob(encoded);
|
|
8
|
+
const bytes = new Uint8ClampedArray(binary.length);
|
|
9
|
+
for (let i = 0; i < binary.length; i++) {
|
|
10
|
+
bytes[i] = binary.charCodeAt(i);
|
|
11
|
+
}
|
|
12
|
+
return bytes;
|
|
13
|
+
}
|
|
2
14
|
function serializeImageData(imageData) {
|
|
3
|
-
const binary = String.fromCharCode(...new Uint8Array(imageData.data.buffer));
|
|
4
|
-
const base64 = btoa(binary);
|
|
5
15
|
return {
|
|
6
16
|
width: imageData.width,
|
|
7
17
|
height: imageData.height,
|
|
8
|
-
data:
|
|
18
|
+
data: base64EncodeArrayBuffer(imageData.data.buffer)
|
|
9
19
|
};
|
|
10
20
|
}
|
|
11
21
|
function serializeNullableImageData(imageData) {
|
|
12
22
|
if (!imageData) return null;
|
|
13
23
|
return serializeImageData(imageData);
|
|
14
24
|
}
|
|
25
|
+
function deserializeRawImageData(serialized) {
|
|
26
|
+
return {
|
|
27
|
+
width: serialized.width,
|
|
28
|
+
height: serialized.height,
|
|
29
|
+
data: base64DecodeArrayBuffer(serialized.data)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
15
32
|
function deserializeImageData(serialized) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < binary.length; i++) {
|
|
19
|
-
bytes[i] = binary.charCodeAt(i);
|
|
20
|
-
}
|
|
21
|
-
return new ImageData(bytes, serialized.width, serialized.height);
|
|
33
|
+
const data = base64DecodeArrayBuffer(serialized.data);
|
|
34
|
+
return new ImageData(data, serialized.width, serialized.height);
|
|
22
35
|
}
|
|
23
36
|
function deserializeNullableImageData(serialized) {
|
|
24
37
|
if (!serialized) return null;
|
|
25
38
|
return deserializeImageData(serialized);
|
|
26
39
|
}
|
|
27
40
|
export {
|
|
41
|
+
base64DecodeArrayBuffer,
|
|
42
|
+
base64EncodeArrayBuffer,
|
|
28
43
|
deserializeImageData,
|
|
29
44
|
deserializeNullableImageData,
|
|
45
|
+
deserializeRawImageData,
|
|
30
46
|
serializeImageData,
|
|
31
47
|
serializeNullableImageData
|
|
32
48
|
};
|
package/dist/index.prod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ImageData/serialization.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../src/ImageData/serialization.ts"],"sourcesContent":["import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'\n\nexport function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {\n const binary = String.fromCharCode(...new Uint8Array(buffer))\n return btoa(binary) as Base64EncodedUInt8Array\n}\n\nexport function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray {\n const binary = atob(encoded)\n const bytes = new Uint8ClampedArray(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n\n/**\n * Serialize for use in JSON. Pixel data is stored as base64 encoded string.\n */\nexport function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData {\n return {\n width: imageData.width,\n height: imageData.height,\n data: base64EncodeArrayBuffer(imageData.data.buffer),\n }\n}\n\nexport function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData {\n if (!imageData) return null as any\n\n return serializeImageData(imageData) as any\n}\n\nexport function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike {\n return {\n width: serialized.width,\n height: serialized.height,\n data: base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array),\n }\n}\n\nexport function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData {\n const data = base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array)\n\n return new ImageData(data as ImageDataArray, serialized.width, serialized.height) as any\n}\n\nexport function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData {\n if (!serialized) return null as any\n return deserializeImageData(serialized) as any\n}\n"],"mappings":";AAEO,SAAS,wBAAwB,QAAkD;AACxF,QAAM,SAAS,OAAO,aAAa,GAAG,IAAI,WAAW,MAAM,CAAC;AAC5D,SAAO,KAAK,MAAM;AACpB;AAEO,SAAS,wBAAwB,SAAqD;AAC3F,QAAM,SAAS,KAAK,OAAO;AAC3B,QAAM,QAAQ,IAAI,kBAAkB,OAAO,MAAM;AACjD,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,SAAO;AACT;AAKO,SAAS,mBAA4C,WAAmC;AAC7F,SAAO;AAAA,IACL,OAAO,UAAU;AAAA,IACjB,QAAQ,UAAU;AAAA,IAClB,MAAM,wBAAwB,UAAU,KAAK,MAAM;AAAA,EACrD;AACF;AAEO,SAAS,2BAA2D,WAA2D;AACpI,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO,mBAAmB,SAAS;AACrC;AAEO,SAAS,wBAAuD,YAA8B;AACnG,SAAO;AAAA,IACL,OAAO,WAAW;AAAA,IAClB,QAAQ,WAAW;AAAA,IACnB,MAAM,wBAAwB,WAAW,IAA+B;AAAA,EAC1E;AACF;AAEO,SAAS,qBAAoD,YAA0B;AAC5F,QAAM,OAAO,wBAAwB,WAAW,IAA+B;AAE/E,SAAO,IAAI,UAAU,MAAwB,WAAW,OAAO,WAAW,MAAM;AAClF;AAEO,SAAS,6BAAmE,YAAkD;AACnI,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO,qBAAqB,UAAU;AACxC;","names":[]}
|
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Color32, ImageDataLike } from '../_types'
|
|
2
|
+
|
|
3
|
+
export function makeImageDataColor32Adapter(imageData: ImageDataLike) {
|
|
4
|
+
const data32 = new Uint32Array(imageData.data.buffer)
|
|
5
|
+
|
|
6
|
+
function inBounds(x: number, y: number) {
|
|
7
|
+
return x < 0 || x >= imageData.width || y < 0 || y >= imageData.height
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function setPixel(
|
|
11
|
+
x: number,
|
|
12
|
+
y: number,
|
|
13
|
+
color: Color32,
|
|
14
|
+
): void {
|
|
15
|
+
if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) return
|
|
16
|
+
data32[y * imageData.width + x] = color
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getPixel(
|
|
20
|
+
x: number,
|
|
21
|
+
y: number,
|
|
22
|
+
): Color32 | undefined {
|
|
23
|
+
if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) return
|
|
24
|
+
|
|
25
|
+
return data32[y * imageData.width + x] as Color32
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
inBounds,
|
|
30
|
+
imageData,
|
|
31
|
+
data32,
|
|
32
|
+
setPixel,
|
|
33
|
+
getPixel,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -1,37 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { Base64EncodedUInt8Array, ImageDataLike, SerializedImageData } from '../_types'
|
|
2
|
+
|
|
3
|
+
export function base64EncodeArrayBuffer(buffer: ArrayBufferLike): Base64EncodedUInt8Array {
|
|
4
|
+
const binary = String.fromCharCode(...new Uint8Array(buffer))
|
|
5
|
+
return btoa(binary) as Base64EncodedUInt8Array
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function base64DecodeArrayBuffer(encoded: Base64EncodedUInt8Array): Uint8ClampedArray {
|
|
9
|
+
const binary = atob(encoded)
|
|
10
|
+
const bytes = new Uint8ClampedArray(binary.length)
|
|
11
|
+
for (let i = 0; i < binary.length; i++) {
|
|
12
|
+
bytes[i] = binary.charCodeAt(i)
|
|
13
|
+
}
|
|
14
|
+
return bytes
|
|
5
15
|
}
|
|
6
16
|
|
|
7
17
|
/**
|
|
8
|
-
* Serialize for use in JSON.
|
|
18
|
+
* Serialize for use in JSON. Pixel data is stored as base64 encoded string.
|
|
9
19
|
*/
|
|
10
|
-
export function serializeImageData<T extends
|
|
11
|
-
const binary = String.fromCharCode(...new Uint8Array(imageData.data.buffer))
|
|
12
|
-
const base64 = btoa(binary)
|
|
13
|
-
|
|
20
|
+
export function serializeImageData<T extends ImageDataLike>(imageData: T): SerializedImageData {
|
|
14
21
|
return {
|
|
15
22
|
width: imageData.width,
|
|
16
23
|
height: imageData.height,
|
|
17
|
-
data:
|
|
24
|
+
data: base64EncodeArrayBuffer(imageData.data.buffer),
|
|
18
25
|
}
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
export function serializeNullableImageData<T extends
|
|
28
|
+
export function serializeNullableImageData<T extends ImageDataLike | null>(imageData: T): T extends null ? null : SerializedImageData {
|
|
22
29
|
if (!imageData) return null as any
|
|
23
30
|
|
|
24
31
|
return serializeImageData(imageData) as any
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
export function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
export function deserializeRawImageData<T extends SerializedImageData>(serialized: T): ImageDataLike {
|
|
35
|
+
return {
|
|
36
|
+
width: serialized.width,
|
|
37
|
+
height: serialized.height,
|
|
38
|
+
data: base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array),
|
|
32
39
|
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function deserializeImageData<T extends SerializedImageData>(serialized: T): ImageData {
|
|
43
|
+
const data = base64DecodeArrayBuffer(serialized.data as Base64EncodedUInt8Array)
|
|
33
44
|
|
|
34
|
-
return new ImageData(
|
|
45
|
+
return new ImageData(data as ImageDataArray, serialized.width, serialized.height) as any
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
export function deserializeNullableImageData<T extends SerializedImageData | null>(serialized: T): T extends null ? null : ImageData {
|
package/src/_types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// ALL values are 0-255 (including alpha which in CSS is 0-1)
|
|
2
|
+
export type RGBA = { r: number, g: number, b: number, a: number }
|
|
3
|
+
|
|
4
|
+
// A 32-bit integer containing r,g,b,a data
|
|
5
|
+
export type Color32 = number & { readonly __brandColor32: unique symbol };
|
|
6
|
+
|
|
7
|
+
// ALL values are floats from 0-1
|
|
8
|
+
export type RGBAFloat = RGBA & { readonly __brandRGBAFloat: unique symbol }
|
|
9
|
+
|
|
10
|
+
export type ImageDataLike = {
|
|
11
|
+
width: number
|
|
12
|
+
height: number
|
|
13
|
+
data: Uint8ClampedArray
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type SerializedImageData = {
|
|
17
|
+
width: number,
|
|
18
|
+
height: number,
|
|
19
|
+
data: string,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Base64EncodedUInt8Array = string & { readonly __brandBase64UInt8Array: unique symbol }
|
package/src/color.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Color32, RGBA } from './_types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Packs RGBA into a 32-bit integer compatible with
|
|
5
|
+
* Little-Endian Uint32Array views on ImageData.
|
|
6
|
+
*/
|
|
7
|
+
export function packColor(r: number, g: number, b: number, a: number): Color32 {
|
|
8
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function packRGBA({ r, g, b, a }: RGBA): Color32 {
|
|
12
|
+
return ((a << 24) | (b << 16) | (g << 8) | r) >>> 0 as Color32
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const unpackRed = (packed: Color32): number => (packed >>> 0) & 0xFF
|
|
16
|
+
export const unpackGreen = (packed: Color32): number => (packed >>> 8) & 0xFF
|
|
17
|
+
export const unpackBlue = (packed: Color32): number => (packed >>> 16) & 0xFF
|
|
18
|
+
export const unpackAlpha = (packed: Color32): number => (packed >>> 24) & 0xFF
|
|
19
|
+
|
|
20
|
+
export function unpackColor(packed: Color32): RGBA {
|
|
21
|
+
return {
|
|
22
|
+
r: (packed >>> 0) & 0xFF,
|
|
23
|
+
g: (packed >>> 8) & 0xFF,
|
|
24
|
+
b: (packed >>> 16) & 0xFF,
|
|
25
|
+
a: (packed >>> 24) & 0xFF,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const SCRATCH_RGBA: RGBA = { r: 0, g: 0, b: 0, a: 0 }
|
|
30
|
+
|
|
31
|
+
// uses a scratch arg for memory perf. Be careful about re-use.
|
|
32
|
+
export function unpackColorTo(packed: Color32, scratch = SCRATCH_RGBA): RGBA {
|
|
33
|
+
scratch.r = (packed >>> 0) & 0xFF
|
|
34
|
+
scratch.g = (packed >>> 8) & 0xFF
|
|
35
|
+
scratch.b = (packed >>> 16) & 0xFF
|
|
36
|
+
scratch.a = (packed >>> 24) & 0xFF
|
|
37
|
+
return scratch
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function colorDistance(a: Color32, b: Color32): number {
|
|
41
|
+
const dr = (a & 0xFF) - (b & 0xFF)
|
|
42
|
+
const dg = ((a >>> 8) & 0xFF) - ((b >>> 8) & 0xFF)
|
|
43
|
+
const db = ((a >>> 16) & 0xFF) - ((b >>> 16) & 0xFF)
|
|
44
|
+
const da = ((a >>> 24) & 0xFF) - ((b >>> 24) & 0xFF)
|
|
45
|
+
return dr * dr + dg * dg + db * db + da * da
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function lerpColor32(a: Color32, b: Color32, t: number): Color32 {
|
|
49
|
+
const r = (a & 0xFF) + t * ((b & 0xFF) - (a & 0xFF))
|
|
50
|
+
const g = ((a >>> 8) & 0xFF) + t * (((b >>> 8) & 0xFF) - ((a >>> 8) & 0xFF))
|
|
51
|
+
const b_ = ((a >>> 16) & 0xFF) + t * (((b >>> 16) & 0xFF) - ((a >>> 16) & 0xFF))
|
|
52
|
+
const a_ = ((a >>> 24) & 0xFF) + t * (((b >>> 24) & 0xFF) - ((a >>> 24) & 0xFF))
|
|
53
|
+
|
|
54
|
+
return packColor(r, g, b_, a_)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Convert 0xAABBGGRR to #RRGGBBAA
|
|
58
|
+
export function color32ToHex(color: Color32): string {
|
|
59
|
+
const r = (color & 0xFF).toString(16).padStart(2, '0')
|
|
60
|
+
const g = ((color >>> 8) & 0xFF).toString(16).padStart(2, '0')
|
|
61
|
+
const b = ((color >>> 16) & 0xFF).toString(16).padStart(2, '0')
|
|
62
|
+
const a = ((color >>> 24) & 0xFF).toString(16).padStart(2, '0')
|
|
63
|
+
return `#${r}${g}${b}${a}`
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Converts a 32-bit integer (0xAABBGGRR) to a CSS rgba() string.
|
|
68
|
+
* Example: 0xFF0000FF -> "rgba(255,0,0,1)"
|
|
69
|
+
*/
|
|
70
|
+
export function color32ToCssRGBA(color: Color32): string {
|
|
71
|
+
const r = color & 0xFF
|
|
72
|
+
const g = (color >>> 8) & 0xFF
|
|
73
|
+
const b = (color >>> 16) & 0xFF
|
|
74
|
+
const a = (color >>> 24) & 0xFF
|
|
75
|
+
|
|
76
|
+
const alpha = Number((a / 255).toFixed(3))
|
|
77
|
+
|
|
78
|
+
return `rgba(${r},${g},${b},${alpha})`
|
|
79
|
+
}
|