@squoosh-kit/resize 0.0.1 → 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 +125 -29
- package/dist/resize.worker.js +2 -2
- package/dist/resize.worker.js.map +3 -3
- package/dist/types.d.ts +19 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/%40squoosh-kit%2Fresize)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Professional image resizing with uncompromising quality**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Transform your images with the same high-quality resizing algorithm that powers Google's Squoosh. Using the Lanczos3 method for mathematically precise scaling, this package delivers crisp, clear results at any size through WebAssembly acceleration.
|
|
8
|
+
|
|
9
|
+
Whether you need thumbnails for a gallery, responsive images for the web, or batch processing for a media library, this package handles resizing with the quality your users deserve, all while keeping your application smooth and responsive.
|
|
8
10
|
|
|
9
11
|
## Installation
|
|
10
12
|
|
|
@@ -16,56 +18,150 @@ npm install @squoosh-kit/resize
|
|
|
16
18
|
|
|
17
19
|
## Quick Start
|
|
18
20
|
|
|
21
|
+
Resize images with confidence:
|
|
22
|
+
|
|
19
23
|
```typescript
|
|
20
24
|
import { resize, createResizer } from '@squoosh-kit/resize';
|
|
21
|
-
import type { ImageInput
|
|
25
|
+
import type { ImageInput } from '@squoosh-kit/resize';
|
|
22
26
|
|
|
23
|
-
//
|
|
24
|
-
const imageData: ImageInput =
|
|
27
|
+
// Your image data - works with any source
|
|
28
|
+
const imageData: ImageInput = {
|
|
29
|
+
data: imageBuffer,
|
|
30
|
+
width: 2048,
|
|
31
|
+
height: 1536
|
|
32
|
+
};
|
|
25
33
|
|
|
26
|
-
//
|
|
34
|
+
// Smart resizing maintains aspect ratio
|
|
35
|
+
const thumbnail = await resize(
|
|
36
|
+
new AbortController().signal,
|
|
37
|
+
imageData,
|
|
38
|
+
{ width: 400 } // height calculated automatically
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Exact dimensions when you need them
|
|
27
42
|
const resizedImage = await resize(
|
|
28
43
|
new AbortController().signal,
|
|
29
|
-
null,
|
|
30
44
|
imageData,
|
|
31
|
-
{ width:
|
|
45
|
+
{ width: 1200, height: 800 }
|
|
32
46
|
);
|
|
33
47
|
|
|
34
|
-
//
|
|
35
|
-
const resizer = createResizer('worker');
|
|
36
|
-
const
|
|
48
|
+
// Create a resizer for batch operations
|
|
49
|
+
const resizer = createResizer('worker');
|
|
50
|
+
const results = await Promise.all([
|
|
51
|
+
resizer(new AbortController().signal, imageData, { width: 800 }),
|
|
52
|
+
resizer(new AbortController().signal, imageData, { width: 1200 }),
|
|
53
|
+
resizer(new AbortController().signal, imageData, { width: 1600 })
|
|
54
|
+
]);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## The Quality Difference
|
|
58
|
+
|
|
59
|
+
This isn't your average image resizer. Under the hood, it uses the Lanczos3 algorithm - a sophisticated mathematical approach that considers surrounding pixels when calculating each new pixel value. The result is resizing that maintains sharpness, avoids artifacts, and preserves the visual quality that matters.
|
|
60
|
+
|
|
61
|
+
All processing happens in WebAssembly for incredible speed, with Web Workers ensuring your main thread stays free for user interactions.
|
|
62
|
+
|
|
63
|
+
## Real-World Examples
|
|
64
|
+
|
|
65
|
+
**Responsive Image Generation**
|
|
66
|
+
```typescript
|
|
67
|
+
// Generate multiple sizes for responsive design
|
|
68
|
+
const sizes = [320, 640, 1024, 1600];
|
|
69
|
+
|
|
70
|
+
const responsiveImages = await Promise.all(
|
|
71
|
+
sizes.map(width =>
|
|
72
|
+
resize(
|
|
73
|
+
new AbortController().signal,
|
|
74
|
+
originalImage,
|
|
75
|
+
{ width, height: Math.round(width * 0.75) }
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Photo Gallery Thumbnails**
|
|
82
|
+
```typescript
|
|
83
|
+
const resizer = createResizer('client'); // Direct for server use
|
|
84
|
+
|
|
85
|
+
for (const photo of photoFiles) {
|
|
86
|
+
const fullImage = await loadImage(photo);
|
|
87
|
+
const thumbnail = await resizer(
|
|
88
|
+
new AbortController().signal,
|
|
89
|
+
fullImage,
|
|
90
|
+
{ width: 300, height: 200 }
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
await saveThumbnail(photo.name, thumbnail);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Dynamic Image Processing**
|
|
98
|
+
```typescript
|
|
99
|
+
// Resize based on user preferences
|
|
100
|
+
const userWidth = getUserPreferredWidth();
|
|
101
|
+
const processedImage = await resize(
|
|
37
102
|
new AbortController().signal,
|
|
38
103
|
imageData,
|
|
39
|
-
{
|
|
104
|
+
{
|
|
105
|
+
width: userWidth,
|
|
106
|
+
linearRGB: true, // Better color accuracy
|
|
107
|
+
premultiply: false // Maintain transparency
|
|
108
|
+
}
|
|
40
109
|
);
|
|
41
110
|
```
|
|
42
111
|
|
|
43
|
-
## API
|
|
112
|
+
## API Reference
|
|
113
|
+
|
|
114
|
+
### `resize(signal, imageData, options)`
|
|
115
|
+
|
|
116
|
+
The main resizing function. Smart defaults make it easy to use.
|
|
44
117
|
|
|
45
|
-
|
|
118
|
+
- `signal` - `AbortSignal` to cancel long operations
|
|
119
|
+
- `imageData` - `ImageInput` object with your pixel data
|
|
120
|
+
- `options` - (optional) `ResizeOptions` for dimensions and quality
|
|
121
|
+
- **Returns** - `Promise<ImageInput>` with resized image data
|
|
46
122
|
|
|
47
|
-
|
|
48
|
-
- `workerBridge`: (Optional) A `WorkerBridge` instance.
|
|
49
|
-
- `imageData`: `ImageInput` to resize.
|
|
50
|
-
- `options`: `ResizeOptions` for resizing.
|
|
51
|
-
- **Returns**: `Promise<ImageInput>`
|
|
123
|
+
### `createResizer(mode?)`
|
|
52
124
|
|
|
53
|
-
|
|
125
|
+
Creates a reusable resizing function for efficient batch processing.
|
|
54
126
|
|
|
55
|
-
- `mode
|
|
56
|
-
- **Returns
|
|
127
|
+
- `mode` - (optional) `'worker'` or `'client'`, defaults to `'worker'`
|
|
128
|
+
- **Returns** - A function with the same signature as `resize()`
|
|
57
129
|
|
|
58
130
|
### `ResizeOptions`
|
|
59
131
|
|
|
132
|
+
Control the quality and behavior of resizing:
|
|
133
|
+
|
|
60
134
|
```typescript
|
|
61
|
-
|
|
62
|
-
width?: number;
|
|
63
|
-
height?: number;
|
|
64
|
-
premultiply?: boolean; // default: false
|
|
65
|
-
linearRGB?: boolean;
|
|
66
|
-
}
|
|
135
|
+
type ResizeOptions = {
|
|
136
|
+
width?: number; // Target width (aspect ratio maintained if only width/height set)
|
|
137
|
+
height?: number; // Target height (aspect ratio maintained if only width/height set)
|
|
138
|
+
premultiply?: boolean; // Premultiply alpha channel (default: false)
|
|
139
|
+
linearRGB?: boolean; // Use linear RGB color space (default: false)
|
|
140
|
+
};
|
|
67
141
|
```
|
|
68
142
|
|
|
143
|
+
## Pro Tips
|
|
144
|
+
|
|
145
|
+
- **Maintain aspect ratio** - Set only width or height, and the other dimension calculates automatically
|
|
146
|
+
- **Use linearRGB for accuracy** - Better color reproduction, especially for photos
|
|
147
|
+
- **Batch with persistent resizers** - More efficient than one-off operations
|
|
148
|
+
- **Workers for UI responsiveness** - Keeps your interface smooth during heavy processing
|
|
149
|
+
|
|
150
|
+
## Perfect For
|
|
151
|
+
|
|
152
|
+
- **Web Applications** - Responsive images, user avatar processing
|
|
153
|
+
- **Media Libraries** - Batch thumbnail generation, format conversion
|
|
154
|
+
- **E-commerce** - Product image optimization, zoom functionality
|
|
155
|
+
- **Content Management** - Automated image processing pipelines
|
|
156
|
+
- **Mobile Apps** - Device-specific image sizing and optimization
|
|
157
|
+
|
|
158
|
+
## Works Everywhere
|
|
159
|
+
|
|
160
|
+
- **Bun** - Optimized performance and full feature support
|
|
161
|
+
- **Node.js** - Perfect for server-side image processing
|
|
162
|
+
- **Browsers** - Web Worker support for responsive user interfaces
|
|
163
|
+
- **TypeScript** - Complete type safety and IntelliSense support
|
|
164
|
+
|
|
69
165
|
## License
|
|
70
166
|
|
|
71
|
-
MIT
|
|
167
|
+
MIT - resize with confidence
|
package/dist/resize.worker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var K=null;async function O(){if(K)return;let
|
|
2
|
+
var K=null;async function O(){if(K)return;let F="./wasm/resize",j=await import.meta.resolve(`${F}/squoosh_resize.js`),x=await import(j);await x.default(fetch(new URL(`${F}/squoosh_resize_bg.wasm`,j))),K=x.resize}async function L(F,j){if(await O(),!K)throw Error("Resize module not initialized");let{data:x,width:G,height:B}=F,E=j.width??G,J=j.height??B;if(j.width&&!j.height)J=Math.round(B*j.width/G);else if(j.height&&!j.width)E=Math.round(G*j.height/B);if(E<=0||J<=0)throw Error("Invalid output dimensions");let N=x instanceof Uint8ClampedArray?new Uint8Array(x):x;return{data:K(N,G,B,E,J,Q(),j.premultiply?1:0,j.linearRGB?1:0),width:E,height:J}}async function T(F,j,x){if(F.aborted)throw new DOMException("Aborted","AbortError");return L(j,x)}function Q(){return 3}if(typeof self<"u")self.onmessage=async(F)=>{let{id:j,type:x,payload:G}=F.data,B={id:j,ok:!1};try{if(x!=="resize:run")throw Error(`Unknown message type: ${x}`);let E=await L(G.image,G.options);B.ok=!0,B.data=E;let J=E.data.buffer;if(J)self.postMessage(B,[J]);else self.postMessage(B)}catch(E){B.error=E instanceof Error?E.message:String(E),self.postMessage(B)}};export{T as resizeClient};
|
|
3
3
|
export{T as a};
|
|
4
4
|
|
|
5
|
-
//# debugId=
|
|
5
|
+
//# debugId=2E07CB4CD56250F364756E2164756E21
|
|
6
6
|
//# sourceMappingURL=resize.worker.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/resize.worker.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Resize processor - single-source worker/client implementation\n */\n\nimport {\n hasImageData,\n type WorkerRequest,\n type WorkerResponse,\n type ImageInput,\n} from '@squoosh-kit/runtime';\nimport type { ResizeOptions } from './types.ts';\n\n// Define the type locally to avoid module resolution issues with the linter\ntype SquooshWasmResize = (\n data: Uint8Array,\n input_width: number,\n input_height: number,\n output_width: number,\n output_height: number,\n typ_idx: number,\n premultiply: number,\n color_space_conversion: number,\n) => Uint8Array;\n\nlet wasmResize: SquooshWasmResize | null = null;\n\nasync function init(): Promise<void> {\n if (wasmResize) {\n return;\n }\n\n const
|
|
5
|
+
"/**\n * Resize processor - single-source worker/client implementation\n */\n\nimport {\n hasImageData,\n type WorkerRequest,\n type WorkerResponse,\n type ImageInput,\n} from '@squoosh-kit/runtime';\nimport type { ResizeOptions } from './types.ts';\n\n// Define the type locally to avoid module resolution issues with the linter\ntype SquooshWasmResize = (\n data: Uint8Array,\n input_width: number,\n input_height: number,\n output_width: number,\n output_height: number,\n typ_idx: number,\n premultiply: number,\n color_space_conversion: number,\n) => Uint8Array;\n\nlet wasmResize: SquooshWasmResize | null = null;\n\nasync function init(): Promise<void> {\n if (wasmResize) {\n return;\n }\n\n const wasmDirectory = './wasm/resize';\n const modulePath = await import.meta.resolve(\n `${wasmDirectory}/squoosh_resize.js`,\n );\n const module = await import(modulePath);\n\n // Squoosh's WASM modules expect to be initialized with promises\n await module.default(\n fetch(new URL(`${wasmDirectory}/squoosh_resize_bg.wasm`, modulePath)),\n );\n wasmResize = module.resize;\n}\n\nasync function _resizeCore(\n image: ImageInput,\n options: ResizeOptions,\n): Promise<ImageInput> {\n await init();\n if (!wasmResize) {\n throw new Error('Resize module not initialized');\n }\n\n const { data, width: inputWidth, height: inputHeight } = image;\n\n let outputWidth = options.width ?? inputWidth;\n let outputHeight = options.height ?? inputHeight;\n\n if (options.width && !options.height) {\n outputHeight = Math.round((inputHeight * options.width) / inputWidth);\n } else if (options.height && !options.width) {\n outputWidth = Math.round((inputWidth * options.height) / inputHeight);\n }\n\n if (outputWidth <= 0 || outputHeight <= 0) {\n throw new Error('Invalid output dimensions');\n }\n\n const dataArray =\n data instanceof Uint8ClampedArray ? new Uint8Array(data) : data;\n\n const result = wasmResize(\n dataArray,\n inputWidth,\n inputHeight,\n outputWidth,\n outputHeight,\n getResizeMethod(),\n options.premultiply ? 1 : 0,\n options.linearRGB ? 1 : 0,\n );\n\n return {\n data: result,\n width: outputWidth,\n height: outputHeight,\n };\n}\n\nexport async function resizeClient(\n signal: AbortSignal,\n image: ImageInput,\n options: ResizeOptions,\n): Promise<ImageInput> {\n if (signal.aborted) {\n throw new DOMException('Aborted', 'AbortError');\n }\n return _resizeCore(image, options);\n}\n\n/**\n * Map ResizeOptions to the typ_idx parameter for the resize function\n * 0: Triangular, 1: Catrom, 2: Mitchell, 3: Lanczos3\n */\nfunction getResizeMethod(): number {\n // Default to Lanczos3 (highest quality)\n return 3;\n}\n\n/**\n * Worker message handler\n */\nif (typeof self !== 'undefined') {\n self.onmessage = async (\n event: MessageEvent<\n WorkerRequest<{ image: ImageInput; options: ResizeOptions }>\n >,\n ) => {\n const { id, type, payload } = event.data;\n\n const response: WorkerResponse<ImageInput> = { id, ok: false };\n\n try {\n if (type !== 'resize:run') {\n throw new Error(`Unknown message type: ${type}`);\n }\n\n const resultImage = await _resizeCore(payload.image, payload.options);\n\n response.ok = true;\n response.data = resultImage;\n\n const transferable = resultImage.data.buffer;\n if (transferable) {\n self.postMessage(response, [transferable as ArrayBuffer]);\n } else {\n self.postMessage(response);\n }\n } catch (error) {\n response.error = error instanceof Error ? error.message : String(error);\n self.postMessage(response);\n }\n };\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AAwBA,IAAI,EAAuC,KAE3C,eAAe,CAAI,EAAkB,CACnC,GAAI,EACF,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAwBA,IAAI,EAAuC,KAE3C,eAAe,CAAI,EAAkB,CACnC,GAAI,EACF,OAGF,IAAM,EAAgB,gBAChB,EAAa,MAAM,YAAY,QACnC,GAAG,qBACL,EACM,EAAS,MAAa,UAG5B,MAAM,EAAO,QACX,MAAM,IAAI,IAAI,GAAG,2BAAwC,CAAU,CAAC,CACtE,EACA,EAAa,EAAO,OAGtB,eAAe,CAAW,CACxB,EACA,EACqB,CAErB,GADA,MAAM,EAAK,EACP,CAAC,EACH,MAAU,MAAM,+BAA+B,EAGjD,IAAQ,OAAM,MAAO,EAAY,OAAQ,GAAgB,EAErD,EAAc,EAAQ,OAAS,EAC/B,EAAe,EAAQ,QAAU,EAErC,GAAI,EAAQ,OAAS,CAAC,EAAQ,OAC5B,EAAe,KAAK,MAAO,EAAc,EAAQ,MAAS,CAAU,EAC/D,QAAI,EAAQ,QAAU,CAAC,EAAQ,MACpC,EAAc,KAAK,MAAO,EAAa,EAAQ,OAAU,CAAW,EAGtE,GAAI,GAAe,GAAK,GAAgB,EACtC,MAAU,MAAM,2BAA2B,EAG7C,IAAM,EACJ,aAAgB,kBAAoB,IAAI,WAAW,CAAI,EAAI,EAa7D,MAAO,CACL,KAZa,EACb,EACA,EACA,EACA,EACA,EACA,EAAgB,EAChB,EAAQ,YAAc,EAAI,EAC1B,EAAQ,UAAY,EAAI,CAC1B,EAIE,MAAO,EACP,OAAQ,CACV,EAGF,eAAsB,CAAY,CAChC,EACA,EACA,EACqB,CACrB,GAAI,EAAO,QACT,MAAM,IAAI,aAAa,UAAW,YAAY,EAEhD,OAAO,EAAY,EAAO,CAAO,EAOnC,SAAS,CAAe,EAAW,CAEjC,MAAO,GAMT,GAAI,OAAO,KAAS,IAClB,KAAK,UAAY,MACf,IAGG,CACH,IAAQ,KAAI,OAAM,WAAY,EAAM,KAE9B,EAAuC,CAAE,KAAI,GAAI,EAAM,EAE7D,GAAI,CACF,GAAI,IAAS,aACX,MAAU,MAAM,yBAAyB,GAAM,EAGjD,IAAM,EAAc,MAAM,EAAY,EAAQ,MAAO,EAAQ,OAAO,EAEpE,EAAS,GAAK,GACd,EAAS,KAAO,EAEhB,IAAM,EAAe,EAAY,KAAK,OACtC,GAAI,EACF,KAAK,YAAY,EAAU,CAAC,CAA2B,CAAC,EAExD,UAAK,YAAY,CAAQ,EAE3B,MAAO,EAAO,CACd,EAAS,MAAQ,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACtE,KAAK,YAAY,CAAQ",
|
|
8
|
+
"debugId": "2E07CB4CD56250F364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type definitions for the Resize package
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Options for resizing an image.
|
|
6
|
+
*/
|
|
7
|
+
export type ResizeOptions = {
|
|
8
|
+
/**
|
|
9
|
+
* The target width of the resized image.
|
|
10
|
+
*/
|
|
5
11
|
width?: number;
|
|
12
|
+
/**
|
|
13
|
+
* The target height of the resized image.
|
|
14
|
+
*/
|
|
6
15
|
height?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Premultiply the alpha channel.
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
7
20
|
premultiply?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Use a linear RGB color space for resizing.
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
8
25
|
linearRGB?: boolean;
|
|
9
|
-
}
|
|
26
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squoosh-kit/resize",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"entry": "src/index.ts",
|
|
6
5
|
"description": "Image resize module for squoosh-kit.",
|
|
7
6
|
"author": "Bartosz Nowak <bnowak008@gmail.com>",
|
|
8
7
|
"license": "MIT",
|
|
@@ -33,9 +32,10 @@
|
|
|
33
32
|
"scripts": {
|
|
34
33
|
"build": "bun run scripts/copy-assets.ts && tsc && bun build --sourcemap --minify --format=esm --splitting --target=bun --outdir=dist src/index.ts src/resize.worker.ts",
|
|
35
34
|
"clean:local": "rm -rf dist *.tsbuildinfo",
|
|
36
|
-
"prepack": "bun run build"
|
|
35
|
+
"prepack": "bun run build && bun test",
|
|
36
|
+
"test": "bun test"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@squoosh-kit/runtime": "0.
|
|
39
|
+
"@squoosh-kit/runtime": "0.0.3"
|
|
40
40
|
}
|
|
41
41
|
}
|