@browser-mc/browser-image-resizer-ex 0.0.1
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 +82 -0
- package/dist/animated-webp.d.ts +40 -0
- package/dist/animated-webp.d.ts.map +1 -0
- package/dist/animated-webp.js +109 -0
- package/dist/animated-webp.js.map +1 -0
- package/dist/frame-utils.d.ts +43 -0
- package/dist/frame-utils.d.ts.map +1 -0
- package/dist/frame-utils.js +158 -0
- package/dist/frame-utils.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @browser-mc/browser-image-resizer-ex
|
|
2
|
+
|
|
3
|
+
Browser-side image resize/convert facade built from the lab packages.
|
|
4
|
+
|
|
5
|
+
- AVIF output through `@browser-mc/webcodecs-avif`
|
|
6
|
+
- Animated WebP output through `@browser-mc/media-container` RIFF `VP8X`/`ANIM`/`ANMF` muxing
|
|
7
|
+
- EXIF keep/drop/drop-GPS through `@browser-mc/exif-transplant`
|
|
8
|
+
- HDR/wide-gamut inspection and raw planar resize through `@browser-mc/webcodecs-color`
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pnpm add @browser-mc/browser-image-resizer-ex
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Convert to AVIF
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { resizeImageToAvif } from '@browser-mc/browser-image-resizer-ex';
|
|
20
|
+
|
|
21
|
+
const result = await resizeImageToAvif(file, {
|
|
22
|
+
width: 1600,
|
|
23
|
+
exif: 'drop-gps',
|
|
24
|
+
quality: 0.82,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await fetch('/upload', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
body: result.blob,
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Generic conversion
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { inspectImageInput, resizeAndConvertImage } from '@browser-mc/browser-image-resizer-ex';
|
|
37
|
+
|
|
38
|
+
const input = await inspectImageInput(file, file.type);
|
|
39
|
+
console.log(input.animated, input.frameCount);
|
|
40
|
+
|
|
41
|
+
const result = await resizeAndConvertImage({
|
|
42
|
+
input: file,
|
|
43
|
+
width: 1024,
|
|
44
|
+
height: 1024,
|
|
45
|
+
fit: 'contain',
|
|
46
|
+
exif: 'keep',
|
|
47
|
+
colorMetadata: 'preserve',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (result.kind === 'animated') {
|
|
51
|
+
console.log(result.frameCount);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(result.resizePath);
|
|
54
|
+
console.log(result.input.colorSpace);
|
|
55
|
+
console.log(result.output?.colorSpace);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`resizeAndConvertImage` detects animated input with `ImageDecoder`. Animated input is preserved as animated WebP by default because animated WebP is currently the only animated output muxer. Use `animation: 'first-frame'` to force still-image conversion.
|
|
60
|
+
|
|
61
|
+
## Animated WebP
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { resizeAnimatedImageToWebp } from '@browser-mc/browser-image-resizer-ex';
|
|
65
|
+
|
|
66
|
+
const result = await resizeAnimatedImageToWebp(file, {
|
|
67
|
+
inputMime: file.type,
|
|
68
|
+
width: 512,
|
|
69
|
+
quality: 0.82,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
console.log(result.frameCount);
|
|
73
|
+
console.log(result.blob);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Notes
|
|
77
|
+
|
|
78
|
+
- Resizing automatically uses raw planar resize for HDR-like formats when `VideoFrame.copyTo()` exposes a supported planar format, and otherwise uses Canvas.
|
|
79
|
+
- `colorMetadata: 'preserve'` is the default. Use `colorMetadata: 'canvas-sdr'` to draw through an sRGB Canvas path and mark the output frame as BT.709 SDR.
|
|
80
|
+
- JPEG/WebP encoding currently goes through `OffscreenCanvas.convertToBlob()`, so strict HDR preservation is not expected there.
|
|
81
|
+
- Animated WebP currently writes full-canvas frames and does not yet optimize changed rectangles.
|
|
82
|
+
- AVIF EXIF writing remuxes to a minimal AVIF structure through `@browser-mc/media-container`. Nonessential original AVIF boxes are not preserved.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { muxAnimatedWebp, type AnimatedWebpFrame, type AnimatedWebpMuxOptions } from '@browser-mc/media-container';
|
|
2
|
+
import { type FrameColorInspection, type ResizeRawOptions } from '@browser-mc/webcodecs-color';
|
|
3
|
+
import { type BrowserImageColorMetadataPolicy } from './frame-utils.js';
|
|
4
|
+
export type { AnimatedWebpFrame, AnimatedWebpMuxOptions, };
|
|
5
|
+
export { muxAnimatedWebp };
|
|
6
|
+
export type BrowserAnimatedImageResizerOptions = {
|
|
7
|
+
input: Blob | ArrayBuffer | Uint8Array;
|
|
8
|
+
inputMime?: string;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
fit?: 'contain' | 'cover' | 'fill';
|
|
12
|
+
rawResizeAlgorithm?: ResizeRawOptions['algorithm'];
|
|
13
|
+
colorMetadata?: BrowserImageColorMetadataPolicy;
|
|
14
|
+
quality?: number;
|
|
15
|
+
loopCount?: number;
|
|
16
|
+
backgroundColor?: AnimatedWebpMuxOptions['backgroundColor'];
|
|
17
|
+
colorSpaceConversion?: ColorSpaceConversion;
|
|
18
|
+
};
|
|
19
|
+
export type BrowserAnimatedImageResizerResult = {
|
|
20
|
+
kind: 'animated';
|
|
21
|
+
data: Uint8Array;
|
|
22
|
+
blob: Blob;
|
|
23
|
+
mime: 'image/webp';
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
frameCount: number;
|
|
27
|
+
input: {
|
|
28
|
+
animated: boolean;
|
|
29
|
+
frameCount: number;
|
|
30
|
+
repetitionCount: number;
|
|
31
|
+
};
|
|
32
|
+
frames: Array<{
|
|
33
|
+
duration: number;
|
|
34
|
+
inspection: FrameColorInspection;
|
|
35
|
+
resizePath: 'none' | 'raw' | 'canvas';
|
|
36
|
+
}>;
|
|
37
|
+
warnings: string[];
|
|
38
|
+
};
|
|
39
|
+
export declare function resizeAnimatedImageToWebp(options: BrowserAnimatedImageResizerOptions): Promise<BrowserAnimatedImageResizerResult>;
|
|
40
|
+
//# sourceMappingURL=animated-webp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animated-webp.d.ts","sourceRoot":"","sources":["../src/animated-webp.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAML,KAAK,+BAA+B,EACrC,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,iBAAiB,EACjB,sBAAsB,GACvB,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,MAAM,MAAM,kCAAkC,GAAG;IAC/C,KAAK,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACnC,kBAAkB,CAAC,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACnD,aAAa,CAAC,EAAE,+BAA+B,CAAC;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,sBAAsB,CAAC,iBAAiB,CAAC,CAAC;IAC5D,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE;QACL,QAAQ,EAAE,OAAO,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,MAAM,EAAE,KAAK,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,oBAAoB,CAAC;QACjC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;KACvC,CAAC,CAAC;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAQF,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,kCAAkC,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAmCvI"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { muxAnimatedWebp, } from '@browser-mc/media-container';
|
|
2
|
+
import { inspectFrame, } from '@browser-mc/webcodecs-color';
|
|
3
|
+
import { copyArrayBuffer, createImageDecoder, encodeFrameWithCanvas, resizeFrameForColor, resolveTargetSize, } from './frame-utils.js';
|
|
4
|
+
export { muxAnimatedWebp };
|
|
5
|
+
export async function resizeAnimatedImageToWebp(options) {
|
|
6
|
+
const decoder = await createAnimatedImageDecoder(options);
|
|
7
|
+
try {
|
|
8
|
+
const track = getSelectedTrack(decoder);
|
|
9
|
+
const size = await resolveAnimationTargetSize(decoder, options);
|
|
10
|
+
const encodedFrames = await encodeAnimationFrames(decoder, track.frameCount, size, options);
|
|
11
|
+
const outputFrames = encodedFrames.map((frame) => frame.muxFrame);
|
|
12
|
+
const data = muxAnimatedWebp({
|
|
13
|
+
width: size.width,
|
|
14
|
+
height: size.height,
|
|
15
|
+
frames: outputFrames,
|
|
16
|
+
loopCount: options.loopCount ?? normalizeRepetitionCount(track.repetitionCount),
|
|
17
|
+
backgroundColor: options.backgroundColor,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
kind: 'animated',
|
|
21
|
+
data,
|
|
22
|
+
blob: new Blob([copyArrayBuffer(data)], { type: 'image/webp' }),
|
|
23
|
+
mime: 'image/webp',
|
|
24
|
+
width: size.width,
|
|
25
|
+
height: size.height,
|
|
26
|
+
frameCount: outputFrames.length,
|
|
27
|
+
input: {
|
|
28
|
+
animated: track.animated,
|
|
29
|
+
frameCount: track.frameCount,
|
|
30
|
+
repetitionCount: track.repetitionCount,
|
|
31
|
+
},
|
|
32
|
+
frames: encodedFrames.map((frame) => frame.result),
|
|
33
|
+
warnings: encodedFrames.flatMap((frame) => frame.warnings),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
decoder.close();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function createAnimatedImageDecoder(options) {
|
|
41
|
+
const inputMime = options.inputMime ?? (options.input instanceof Blob && options.input.type ? options.input.type : 'image/webp');
|
|
42
|
+
return createImageDecoder(options.input, inputMime, {
|
|
43
|
+
preferAnimation: true,
|
|
44
|
+
colorSpaceConversion: options.colorSpaceConversion ?? 'none',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function getSelectedTrack(decoder) {
|
|
48
|
+
const track = decoder.tracks.selectedTrack;
|
|
49
|
+
if (!track)
|
|
50
|
+
throw new Error('ImageDecoder did not expose a selected image track');
|
|
51
|
+
if (!Number.isFinite(track.frameCount) || track.frameCount <= 0)
|
|
52
|
+
throw new Error(`Unsupported animated image frameCount: ${track.frameCount}`);
|
|
53
|
+
return track;
|
|
54
|
+
}
|
|
55
|
+
async function resolveAnimationTargetSize(decoder, options) {
|
|
56
|
+
const frame = (await decoder.decode({ frameIndex: 0, completeFramesOnly: true })).image;
|
|
57
|
+
try {
|
|
58
|
+
return resolveTargetSize(frame.displayWidth, frame.displayHeight, options);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
frame.close();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function encodeAnimationFrames(decoder, frameCount, size, options) {
|
|
65
|
+
const frames = [];
|
|
66
|
+
for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
67
|
+
frames.push(await encodeAnimationFrame(decoder, frameIndex, size, options));
|
|
68
|
+
}
|
|
69
|
+
return frames;
|
|
70
|
+
}
|
|
71
|
+
async function encodeAnimationFrame(decoder, frameIndex, size, options) {
|
|
72
|
+
const frame = (await decoder.decode({ frameIndex, completeFramesOnly: true })).image;
|
|
73
|
+
try {
|
|
74
|
+
const resized = await resizeFrameForColor(frame, size, options);
|
|
75
|
+
try {
|
|
76
|
+
const duration = durationMilliseconds(frame);
|
|
77
|
+
return {
|
|
78
|
+
muxFrame: {
|
|
79
|
+
data: await encodeFrameWithCanvas(resized.frame, 'image/webp', options.quality),
|
|
80
|
+
width: size.width,
|
|
81
|
+
height: size.height,
|
|
82
|
+
duration,
|
|
83
|
+
blend: false,
|
|
84
|
+
dispose: true,
|
|
85
|
+
},
|
|
86
|
+
result: {
|
|
87
|
+
duration,
|
|
88
|
+
inspection: inspectFrame(resized.frame),
|
|
89
|
+
resizePath: resized.path,
|
|
90
|
+
},
|
|
91
|
+
warnings: resized.warnings,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (resized.frame !== frame)
|
|
96
|
+
resized.frame.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
frame.close();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function durationMilliseconds(frame) {
|
|
104
|
+
return Math.max(1, Math.round((frame.duration ?? 100_000) / 1000));
|
|
105
|
+
}
|
|
106
|
+
function normalizeRepetitionCount(repetitionCount) {
|
|
107
|
+
return Number.isFinite(repetitionCount) && repetitionCount > 0 ? repetitionCount : 0;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=animated-webp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animated-webp.js","sourceRoot":"","sources":["../src/animated-webp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ResizeRawOptions } from '@browser-mc/webcodecs-color';
|
|
2
|
+
import { copyArrayBuffer } from '@browser-mc/binary';
|
|
3
|
+
export { copyArrayBuffer };
|
|
4
|
+
export type BrowserImageResizeFit = 'contain' | 'cover' | 'fill';
|
|
5
|
+
export type BrowserImageResizePath = 'none' | 'raw' | 'canvas';
|
|
6
|
+
export type BrowserImageColorMetadataPolicy = 'preserve' | 'canvas-sdr';
|
|
7
|
+
export type FrameResizeOptions = {
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
fit?: BrowserImageResizeFit;
|
|
11
|
+
rawResizeAlgorithm?: ResizeRawOptions['algorithm'];
|
|
12
|
+
colorMetadata?: BrowserImageColorMetadataPolicy;
|
|
13
|
+
};
|
|
14
|
+
export type DecodeImageFrameOptions = {
|
|
15
|
+
colorSpaceConversion?: ColorSpaceConversion;
|
|
16
|
+
desiredWidth?: number;
|
|
17
|
+
desiredHeight?: number;
|
|
18
|
+
preferAnimation?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type ImageInputInspection = {
|
|
21
|
+
animated: boolean;
|
|
22
|
+
frameCount: number;
|
|
23
|
+
repetitionCount: number;
|
|
24
|
+
decoder: 'image-decoder' | 'fallback';
|
|
25
|
+
};
|
|
26
|
+
export declare function createImageDecoder(input: Blob | ArrayBuffer | Uint8Array, type: string, options?: DecodeImageFrameOptions): Promise<ImageDecoder>;
|
|
27
|
+
export declare function inspectImageTrack(input: Blob | ArrayBuffer | Uint8Array, type: string, options?: Pick<DecodeImageFrameOptions, 'colorSpaceConversion'>): Promise<ImageInputInspection>;
|
|
28
|
+
export declare function decodeFirstImageFrame(input: Blob | ArrayBuffer | Uint8Array, type: string, options?: DecodeImageFrameOptions): Promise<VideoFrame>;
|
|
29
|
+
export declare function resolveTargetSize(sourceWidth: number, sourceHeight: number, options: Pick<FrameResizeOptions, 'width' | 'height' | 'fit'>): {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
export declare function resizeFrameForColor(frame: VideoFrame, size: {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}, options: Pick<FrameResizeOptions, 'rawResizeAlgorithm' | 'colorMetadata'>): Promise<{
|
|
37
|
+
frame: VideoFrame;
|
|
38
|
+
path: BrowserImageResizePath;
|
|
39
|
+
warnings: string[];
|
|
40
|
+
}>;
|
|
41
|
+
export declare function encodeFrameWithCanvas(frame: VideoFrame, mime: 'image/jpeg' | 'image/webp', quality?: number): Promise<Uint8Array<ArrayBufferLike>>;
|
|
42
|
+
export declare function toUint8Array(input: Blob | ArrayBuffer | Uint8Array): Promise<Uint8Array>;
|
|
43
|
+
//# sourceMappingURL=frame-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-utils.d.ts","sourceRoot":"","sources":["../src/frame-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjE,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE/D,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,YAAY,CAAC;AAExE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,qBAAqB,CAAC;IAC5B,kBAAkB,CAAC,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACnD,aAAa,CAAC,EAAE,+BAA+B,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,eAAe,GAAG,UAAU,CAAC;CACvC,CAAC;AAEF,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,EACtC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,uBAA4B,yBActC;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,EACtC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,IAAI,CAAC,uBAAuB,EAAE,sBAAsB,CAAM,GAClE,OAAO,CAAC,oBAAoB,CAAC,CAsB/B;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,EACtC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,uBAA4B,uBAgBtC;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;;;EAsBzI;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACvC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,oBAAoB,GAAG,eAAe,CAAC,GACxE,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,sBAAsB,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAyClF;AAED,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,GAAG,YAAY,EAAE,OAAO,SAAO,wCAO/G;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAI9F"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { classifyFrameColor, convertFrameToCanvasSdr, resizeFrameRaw, resizeFrameWithCanvas, } from '@browser-mc/webcodecs-color';
|
|
2
|
+
import { copyArrayBuffer } from '@browser-mc/binary';
|
|
3
|
+
export { copyArrayBuffer };
|
|
4
|
+
export async function createImageDecoder(input, type, options = {}) {
|
|
5
|
+
assertImageDecoder();
|
|
6
|
+
const bytes = await toUint8Array(input);
|
|
7
|
+
const decoder = new ImageDecoder({
|
|
8
|
+
data: copyArrayBuffer(bytes),
|
|
9
|
+
type,
|
|
10
|
+
colorSpaceConversion: options.colorSpaceConversion ?? 'none',
|
|
11
|
+
desiredWidth: options.desiredWidth,
|
|
12
|
+
desiredHeight: options.desiredHeight,
|
|
13
|
+
preferAnimation: options.preferAnimation,
|
|
14
|
+
});
|
|
15
|
+
await decoder.tracks.ready;
|
|
16
|
+
return decoder;
|
|
17
|
+
}
|
|
18
|
+
export async function inspectImageTrack(input, type, options = {}) {
|
|
19
|
+
if (typeof ImageDecoder === 'undefined') {
|
|
20
|
+
return { animated: false, frameCount: 1, repetitionCount: 0, decoder: 'fallback' };
|
|
21
|
+
}
|
|
22
|
+
const decoder = await createImageDecoder(input, type, {
|
|
23
|
+
colorSpaceConversion: options.colorSpaceConversion,
|
|
24
|
+
preferAnimation: true,
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
const track = decoder.tracks.selectedTrack;
|
|
28
|
+
if (!track)
|
|
29
|
+
return { animated: false, frameCount: 1, repetitionCount: 0, decoder: 'image-decoder' };
|
|
30
|
+
const frameCount = Number.isFinite(track.frameCount) && track.frameCount > 0 ? track.frameCount : 1;
|
|
31
|
+
return {
|
|
32
|
+
animated: track.animated || frameCount > 1,
|
|
33
|
+
frameCount,
|
|
34
|
+
repetitionCount: track.repetitionCount,
|
|
35
|
+
decoder: 'image-decoder',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
decoder.close();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function decodeFirstImageFrame(input, type, options = {}) {
|
|
43
|
+
if (typeof ImageDecoder !== 'undefined') {
|
|
44
|
+
try {
|
|
45
|
+
const decoder = await createImageDecoder(input, type, options);
|
|
46
|
+
try {
|
|
47
|
+
return (await decoder.decode({ frameIndex: 0, completeFramesOnly: true })).image;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
decoder.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (typeof createImageBitmap === 'undefined')
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return decodeFirstImageFrameWithBitmap(input, type);
|
|
59
|
+
}
|
|
60
|
+
export function resolveTargetSize(sourceWidth, sourceHeight, options) {
|
|
61
|
+
if (options.width === undefined && options.height === undefined) {
|
|
62
|
+
return { width: sourceWidth, height: sourceHeight };
|
|
63
|
+
}
|
|
64
|
+
if (options.width !== undefined && options.height === undefined) {
|
|
65
|
+
return { width: options.width, height: Math.max(1, Math.round(sourceHeight * options.width / sourceWidth)) };
|
|
66
|
+
}
|
|
67
|
+
if (options.height !== undefined && options.width === undefined) {
|
|
68
|
+
return { width: Math.max(1, Math.round(sourceWidth * options.height / sourceHeight)), height: options.height };
|
|
69
|
+
}
|
|
70
|
+
const boxWidth = options.width ?? sourceWidth;
|
|
71
|
+
const boxHeight = options.height ?? sourceHeight;
|
|
72
|
+
if ((options.fit ?? 'contain') === 'fill')
|
|
73
|
+
return { width: boxWidth, height: boxHeight };
|
|
74
|
+
const scale = (options.fit ?? 'contain') === 'cover'
|
|
75
|
+
? Math.max(boxWidth / sourceWidth, boxHeight / sourceHeight)
|
|
76
|
+
: Math.min(boxWidth / sourceWidth, boxHeight / sourceHeight);
|
|
77
|
+
return {
|
|
78
|
+
width: Math.max(1, Math.round(sourceWidth * scale)),
|
|
79
|
+
height: Math.max(1, Math.round(sourceHeight * scale)),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export async function resizeFrameForColor(frame, size, options) {
|
|
83
|
+
const colorMetadata = options.colorMetadata ?? 'preserve';
|
|
84
|
+
if (size.width === frame.displayWidth && size.height === frame.displayHeight) {
|
|
85
|
+
if (colorMetadata === 'canvas-sdr') {
|
|
86
|
+
return { frame: convertFrameToCanvasSdr(frame).frame, path: 'canvas', warnings: [] };
|
|
87
|
+
}
|
|
88
|
+
return { frame, path: 'none', warnings: [] };
|
|
89
|
+
}
|
|
90
|
+
const warnings = [];
|
|
91
|
+
const color = classifyFrameColor(frame);
|
|
92
|
+
if (colorMetadata === 'canvas-sdr') {
|
|
93
|
+
const canvasResized = resizeFrameWithCanvas(frame, { ...size, colorSpace: 'srgb' });
|
|
94
|
+
try {
|
|
95
|
+
const converted = convertFrameToCanvasSdr(canvasResized.frame);
|
|
96
|
+
if (color.recommendedPath === 'raw-hdr') {
|
|
97
|
+
warnings.push('Canvas SDR conversion uses the browser Canvas sRGB path for HDR/BT.2020 content.');
|
|
98
|
+
}
|
|
99
|
+
return { frame: converted.frame, path: 'canvas', warnings };
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
canvasResized.frame.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (color.recommendedPath === 'raw-hdr') {
|
|
106
|
+
try {
|
|
107
|
+
const resized = await resizeFrameRaw(frame, {
|
|
108
|
+
...size,
|
|
109
|
+
algorithm: options.rawResizeAlgorithm ?? 'bilinear',
|
|
110
|
+
});
|
|
111
|
+
return { frame: resized.frame, path: 'raw', warnings };
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
warnings.push(`raw resize was not available and Canvas fallback was used: ${error instanceof Error ? error.message : String(error)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const resized = resizeFrameWithCanvas(frame, size);
|
|
118
|
+
if (color.recommendedPath === 'raw-hdr') {
|
|
119
|
+
warnings.push('Canvas fallback may collapse HDR/BT.2020 content to sRGB or Display P3.');
|
|
120
|
+
}
|
|
121
|
+
return { frame: resized.frame, path: 'canvas', warnings };
|
|
122
|
+
}
|
|
123
|
+
export async function encodeFrameWithCanvas(frame, mime, quality = 0.85) {
|
|
124
|
+
const canvas = new OffscreenCanvas(frame.displayWidth, frame.displayHeight);
|
|
125
|
+
const context = canvas.getContext('2d', { colorSpace: classifyFrameColor(frame).canvasColorSpace });
|
|
126
|
+
if (!context)
|
|
127
|
+
throw new Error('Could not create 2D canvas context');
|
|
128
|
+
context.drawImage(frame, 0, 0);
|
|
129
|
+
const blob = await canvas.convertToBlob({ type: mime, quality });
|
|
130
|
+
return toUint8Array(blob);
|
|
131
|
+
}
|
|
132
|
+
export async function toUint8Array(input) {
|
|
133
|
+
if (input instanceof Uint8Array)
|
|
134
|
+
return input;
|
|
135
|
+
if (input instanceof ArrayBuffer)
|
|
136
|
+
return new Uint8Array(input);
|
|
137
|
+
return new Uint8Array(await input.arrayBuffer());
|
|
138
|
+
}
|
|
139
|
+
function assertImageDecoder() {
|
|
140
|
+
if (typeof ImageDecoder === 'undefined')
|
|
141
|
+
throw new Error('ImageDecoder API is not available in this environment');
|
|
142
|
+
}
|
|
143
|
+
async function decodeFirstImageFrameWithBitmap(input, type) {
|
|
144
|
+
if (typeof createImageBitmap === 'undefined' || typeof VideoFrame === 'undefined') {
|
|
145
|
+
throw new Error('ImageDecoder API is not available and createImageBitmap/VideoFrame fallback cannot be used');
|
|
146
|
+
}
|
|
147
|
+
const blob = input instanceof Blob
|
|
148
|
+
? input
|
|
149
|
+
: new Blob([copyArrayBuffer(await toUint8Array(input))], { type });
|
|
150
|
+
const bitmap = await createImageBitmap(blob);
|
|
151
|
+
try {
|
|
152
|
+
return new VideoFrame(bitmap, { timestamp: 0 });
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
bitmap.close();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=frame-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-utils.js","sourceRoot":"","sources":["../src/frame-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type ExifPayload } from '@browser-mc/exif-transplant';
|
|
2
|
+
import { type EncodeAvifOptions } from '@browser-mc/webcodecs-avif';
|
|
3
|
+
import { type BrowserImageColorMetadataPolicy, type BrowserImageResizeFit, type BrowserImageResizePath, type ImageInputInspection } from './frame-utils.js';
|
|
4
|
+
import { muxAnimatedWebp, type BrowserAnimatedImageResizerOptions, type BrowserAnimatedImageResizerResult, type AnimatedWebpFrame, type AnimatedWebpMuxOptions } from './animated-webp.js';
|
|
5
|
+
import { type FrameColorClassification, type FrameColorInspection, type ResizeRawOptions } from '@browser-mc/webcodecs-color';
|
|
6
|
+
export type BrowserImageOutputMime = 'image/avif' | 'image/jpeg' | 'image/webp';
|
|
7
|
+
export type BrowserImageExifPolicy = 'keep' | 'drop' | 'drop-gps';
|
|
8
|
+
export type BrowserImageAnimationPolicy = 'preserve' | 'first-frame' | 'error';
|
|
9
|
+
export type { AnimatedWebpFrame, AnimatedWebpMuxOptions, BrowserAnimatedImageResizerOptions, BrowserAnimatedImageResizerResult, BrowserImageColorMetadataPolicy, BrowserImageResizeFit, BrowserImageResizePath, ImageInputInspection, };
|
|
10
|
+
export type BrowserImageResizerOptions = {
|
|
11
|
+
input: Blob | ArrayBuffer | Uint8Array;
|
|
12
|
+
inputMime?: string;
|
|
13
|
+
outputMime?: BrowserImageOutputMime;
|
|
14
|
+
width?: number;
|
|
15
|
+
height?: number;
|
|
16
|
+
fit?: BrowserImageResizeFit;
|
|
17
|
+
exif?: BrowserImageExifPolicy;
|
|
18
|
+
animation?: BrowserImageAnimationPolicy;
|
|
19
|
+
colorSpaceConversion?: ColorSpaceConversion;
|
|
20
|
+
rawResizeAlgorithm?: ResizeRawOptions['algorithm'];
|
|
21
|
+
colorMetadata?: BrowserImageColorMetadataPolicy;
|
|
22
|
+
quality?: number;
|
|
23
|
+
avif?: Omit<EncodeAvifOptions, 'width' | 'height' | 'quality'>;
|
|
24
|
+
};
|
|
25
|
+
export type BrowserImageResizerResult = {
|
|
26
|
+
kind: 'still';
|
|
27
|
+
data: Uint8Array;
|
|
28
|
+
blob: Blob;
|
|
29
|
+
mime: BrowserImageOutputMime;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
resizePath: BrowserImageResizePath;
|
|
33
|
+
input: FrameColorInspection;
|
|
34
|
+
output: FrameColorInspection | null;
|
|
35
|
+
color: FrameColorClassification;
|
|
36
|
+
exif: {
|
|
37
|
+
policy: BrowserImageExifPolicy;
|
|
38
|
+
source: ExifPayload | null;
|
|
39
|
+
written: boolean;
|
|
40
|
+
};
|
|
41
|
+
warnings: string[];
|
|
42
|
+
};
|
|
43
|
+
export type BrowserImageConversionResult = BrowserImageResizerResult | BrowserAnimatedImageResizerResult;
|
|
44
|
+
export declare function inspectImageInput(input: Blob | ArrayBuffer | Uint8Array, inputMime?: string, options?: Pick<BrowserImageResizerOptions, 'colorSpaceConversion'>): Promise<ImageInputInspection & {
|
|
45
|
+
mime: string;
|
|
46
|
+
}>;
|
|
47
|
+
export declare function resizeAndConvertImage(options: BrowserImageResizerOptions): Promise<BrowserImageConversionResult>;
|
|
48
|
+
export declare function resizeImageToAvif(input: Blob | ArrayBuffer | Uint8Array, options?: Omit<BrowserImageResizerOptions, 'input' | 'outputMime'>): Promise<BrowserImageResizerResult>;
|
|
49
|
+
export declare function resizeAnimatedImageToWebp(input: Blob | ArrayBuffer | Uint8Array, options?: Omit<BrowserAnimatedImageResizerOptions, 'input'>): Promise<BrowserAnimatedImageResizerResult>;
|
|
50
|
+
export { muxAnimatedWebp };
|
|
51
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { detectImageMime, readExif, stripGpsFromExif, writeExif, } from '@browser-mc/exif-transplant';
|
|
2
|
+
import { encodeImageToAvif } from '@browser-mc/webcodecs-avif';
|
|
3
|
+
import { copyArrayBuffer, decodeFirstImageFrame, encodeFrameWithCanvas, inspectImageTrack, resizeFrameForColor, resolveTargetSize, toUint8Array, } from './frame-utils.js';
|
|
4
|
+
import { resizeAnimatedImageToWebp as resizeAnimatedImageToWebpInternal, muxAnimatedWebp, } from './animated-webp.js';
|
|
5
|
+
import { classifyFrameColor, inspectFrame, } from '@browser-mc/webcodecs-color';
|
|
6
|
+
export async function inspectImageInput(input, inputMime, options = {}) {
|
|
7
|
+
const inputBytes = await toUint8Array(input);
|
|
8
|
+
const mime = inputMime ?? detectInputMime(inputBytes, input);
|
|
9
|
+
return {
|
|
10
|
+
mime,
|
|
11
|
+
...await inspectImageTrack(inputBytes, mime, {
|
|
12
|
+
colorSpaceConversion: options.colorSpaceConversion ?? 'none',
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export async function resizeAndConvertImage(options) {
|
|
17
|
+
const inputBytes = await toUint8Array(options.input);
|
|
18
|
+
const inputMime = options.inputMime ?? detectInputMime(inputBytes, options.input);
|
|
19
|
+
const inputInfo = await inspectImageTrack(inputBytes, inputMime, {
|
|
20
|
+
colorSpaceConversion: options.colorSpaceConversion ?? 'none',
|
|
21
|
+
});
|
|
22
|
+
const outputMime = options.outputMime ?? defaultOutputMime(inputMime, inputInfo);
|
|
23
|
+
if (inputInfo.animated && (options.animation ?? 'preserve') !== 'first-frame') {
|
|
24
|
+
if ((options.animation ?? 'preserve') === 'error')
|
|
25
|
+
throw new Error('Animated image input is not accepted by this conversion');
|
|
26
|
+
if (outputMime !== 'image/webp') {
|
|
27
|
+
throw new Error('Animated image conversion currently only supports image/webp output');
|
|
28
|
+
}
|
|
29
|
+
return resizeAnimatedImageToWebpInternal({
|
|
30
|
+
input: inputBytes,
|
|
31
|
+
inputMime,
|
|
32
|
+
width: options.width,
|
|
33
|
+
height: options.height,
|
|
34
|
+
fit: options.fit,
|
|
35
|
+
rawResizeAlgorithm: options.rawResizeAlgorithm,
|
|
36
|
+
quality: options.quality,
|
|
37
|
+
colorSpaceConversion: options.colorSpaceConversion,
|
|
38
|
+
colorMetadata: options.colorMetadata,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const exifPolicy = options.exif ?? 'keep';
|
|
42
|
+
const sourceExif = readSourceExif(inputBytes);
|
|
43
|
+
const frame = await decodeFirstImageFrame(inputBytes, inputMime, {
|
|
44
|
+
colorSpaceConversion: options.colorSpaceConversion ?? 'none',
|
|
45
|
+
});
|
|
46
|
+
try {
|
|
47
|
+
const inputInspection = inspectFrame(frame);
|
|
48
|
+
const color = classifyFrameColor(inputInspection);
|
|
49
|
+
const size = resolveTargetSize(frame.displayWidth, frame.displayHeight, options);
|
|
50
|
+
const resized = await resizeFrameForColor(frame, size, options);
|
|
51
|
+
try {
|
|
52
|
+
const encoded = await encodeFrame(resized.frame, outputMime, {
|
|
53
|
+
quality: options.quality,
|
|
54
|
+
avif: options.avif,
|
|
55
|
+
});
|
|
56
|
+
const withExif = applyExifPolicy(encoded, outputMime, sourceExif, exifPolicy);
|
|
57
|
+
const blob = new Blob([copyArrayBuffer(withExif)], { type: outputMime });
|
|
58
|
+
return {
|
|
59
|
+
kind: 'still',
|
|
60
|
+
data: withExif,
|
|
61
|
+
blob,
|
|
62
|
+
mime: outputMime,
|
|
63
|
+
width: size.width,
|
|
64
|
+
height: size.height,
|
|
65
|
+
resizePath: resized.path,
|
|
66
|
+
input: inputInspection,
|
|
67
|
+
output: resized.path === 'none' ? inputInspection : inspectFrame(resized.frame),
|
|
68
|
+
color,
|
|
69
|
+
exif: {
|
|
70
|
+
policy: exifPolicy,
|
|
71
|
+
source: sourceExif,
|
|
72
|
+
written: sourceExif !== null && exifPolicy !== 'drop',
|
|
73
|
+
},
|
|
74
|
+
warnings: resized.warnings,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
if (resized.frame !== frame)
|
|
79
|
+
resized.frame.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
frame.close();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export async function resizeImageToAvif(input, options = {}) {
|
|
87
|
+
const result = await resizeAndConvertImage({ ...options, input, outputMime: 'image/avif' });
|
|
88
|
+
if (result.kind !== 'still')
|
|
89
|
+
throw new Error('resizeImageToAvif does not support animated output');
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
export async function resizeAnimatedImageToWebp(input, options = {}) {
|
|
93
|
+
return resizeAnimatedImageToWebpInternal({ ...options, input });
|
|
94
|
+
}
|
|
95
|
+
export { muxAnimatedWebp };
|
|
96
|
+
function readSourceExif(data) {
|
|
97
|
+
try {
|
|
98
|
+
return readExif(data);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function detectInputMime(data, input) {
|
|
105
|
+
if (input instanceof Blob && input.type)
|
|
106
|
+
return input.type;
|
|
107
|
+
try {
|
|
108
|
+
return detectImageMime(data);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new Error('inputMime is required for image formats without EXIF transplant support');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function defaultOutputMime(inputMime, inputInfo) {
|
|
115
|
+
if (inputInfo?.animated)
|
|
116
|
+
return 'image/webp';
|
|
117
|
+
return inputMime === 'image/webp' || inputMime === 'image/jpeg' || inputMime === 'image/avif'
|
|
118
|
+
? inputMime
|
|
119
|
+
: 'image/avif';
|
|
120
|
+
}
|
|
121
|
+
async function encodeFrame(frame, mime, options) {
|
|
122
|
+
if (mime === 'image/avif') {
|
|
123
|
+
return encodeImageToAvif(frame, {
|
|
124
|
+
...options.avif,
|
|
125
|
+
width: frame.displayWidth,
|
|
126
|
+
height: frame.displayHeight,
|
|
127
|
+
quality: options.quality,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return encodeFrameWithCanvas(frame, mime, options.quality);
|
|
131
|
+
}
|
|
132
|
+
function applyExifPolicy(data, mime, sourceExif, policy) {
|
|
133
|
+
if (policy === 'drop' || !sourceExif)
|
|
134
|
+
return data;
|
|
135
|
+
const exifBytes = policy === 'drop-gps' ? stripGpsFromExif(sourceExif.bytes) : sourceExif.bytes;
|
|
136
|
+
return writeExif(data, exifBytes, mime);
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@browser-mc/browser-image-resizer-ex",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@browser-mc/binary": "^0.0.1",
|
|
15
|
+
"@browser-mc/exif-transplant": "^0.0.1",
|
|
16
|
+
"@browser-mc/media-container": "^0.0.1",
|
|
17
|
+
"@browser-mc/webcodecs-avif": "^0.0.1",
|
|
18
|
+
"@browser-mc/webcodecs-color": "^0.0.1"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"prebuild": "pnpm --filter @browser-mc/binary build && pnpm --filter @browser-mc/media-container build && pnpm --filter @browser-mc/webcodecs-avif build && pnpm --filter @browser-mc/webcodecs-color build && pnpm --filter @browser-mc/exif-transplant build",
|
|
29
|
+
"build": "tsc -p tsconfig.json",
|
|
30
|
+
"pretypecheck": "pnpm --filter @browser-mc/binary build && pnpm --filter @browser-mc/media-container build && pnpm --filter @browser-mc/webcodecs-avif build && pnpm --filter @browser-mc/webcodecs-color build && pnpm --filter @browser-mc/exif-transplant build",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
32
|
+
"test:electron": "pnpm build && env -u ELECTRON_RUN_AS_NODE xvfb-run -a node test/electron-smoke.mjs",
|
|
33
|
+
"test": "pnpm build"
|
|
34
|
+
}
|
|
35
|
+
}
|