@metools/web-image-converter 1.0.0 → 1.0.2
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.d.ts +153 -0
- package/dist/index.js +4 -4
- package/package.json +1 -4
- package/readme.md +181 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
interface CompressionOptions {
|
|
2
|
+
format: string;
|
|
3
|
+
quality?: number;
|
|
4
|
+
}
|
|
5
|
+
declare abstract class ImageCompressionOptions {
|
|
6
|
+
abstract get optionExport(): CompressionOptions;
|
|
7
|
+
}
|
|
8
|
+
declare class JpegCompressionOptions extends ImageCompressionOptions {
|
|
9
|
+
quality: number;
|
|
10
|
+
constructor(quality: number);
|
|
11
|
+
get optionExport(): CompressionOptions;
|
|
12
|
+
}
|
|
13
|
+
declare class PngCompressionOptions extends ImageCompressionOptions {
|
|
14
|
+
quality: number;
|
|
15
|
+
constructor(quality: number);
|
|
16
|
+
get optionExport(): CompressionOptions;
|
|
17
|
+
}
|
|
18
|
+
declare class GifCompressionOptions extends ImageCompressionOptions {
|
|
19
|
+
get optionExport(): CompressionOptions;
|
|
20
|
+
}
|
|
21
|
+
declare class BmpCompressionOptions extends ImageCompressionOptions {
|
|
22
|
+
get optionExport(): CompressionOptions;
|
|
23
|
+
}
|
|
24
|
+
declare class TgaCompressionOptions extends ImageCompressionOptions {
|
|
25
|
+
get optionExport(): CompressionOptions;
|
|
26
|
+
}
|
|
27
|
+
declare class TiffCompressionOptions extends ImageCompressionOptions {
|
|
28
|
+
get optionExport(): CompressionOptions;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface CropDimensionOptions {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}
|
|
37
|
+
interface CropAspectRatioOptions {
|
|
38
|
+
ratio_width: number;
|
|
39
|
+
ratio_height: number;
|
|
40
|
+
}
|
|
41
|
+
interface CropEachSideOptions {
|
|
42
|
+
crop_left: number;
|
|
43
|
+
crop_right: number;
|
|
44
|
+
crop_top: number;
|
|
45
|
+
crop_bottom: number;
|
|
46
|
+
}
|
|
47
|
+
type CropOptions = CropDimensionOptions | CropAspectRatioOptions | CropEachSideOptions;
|
|
48
|
+
declare abstract class ImageCropOptions {
|
|
49
|
+
abstract get optionExport(): CropOptions;
|
|
50
|
+
}
|
|
51
|
+
declare class ImageCropDimensionOptions extends ImageCropOptions {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
width: number;
|
|
55
|
+
height: number;
|
|
56
|
+
constructor(payload: {
|
|
57
|
+
x: number;
|
|
58
|
+
y: number;
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
});
|
|
62
|
+
get optionExport(): CropDimensionOptions;
|
|
63
|
+
}
|
|
64
|
+
declare class ImageCropAspectRatioOptions extends ImageCropOptions {
|
|
65
|
+
ratio_width: number;
|
|
66
|
+
ratio_height: number;
|
|
67
|
+
constructor(payload: {
|
|
68
|
+
ratio_width: number;
|
|
69
|
+
ratio_height: number;
|
|
70
|
+
});
|
|
71
|
+
get optionExport(): CropAspectRatioOptions;
|
|
72
|
+
}
|
|
73
|
+
declare class ImageCropEachSideOptions extends ImageCropOptions {
|
|
74
|
+
crop_left: number;
|
|
75
|
+
crop_right: number;
|
|
76
|
+
crop_top: number;
|
|
77
|
+
crop_bottom: number;
|
|
78
|
+
constructor(payload: {
|
|
79
|
+
crop_left: number;
|
|
80
|
+
crop_right: number;
|
|
81
|
+
crop_top: number;
|
|
82
|
+
crop_bottom: number;
|
|
83
|
+
});
|
|
84
|
+
get optionExport(): CropEachSideOptions;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface ResizeDimensionOptions {
|
|
88
|
+
width: number;
|
|
89
|
+
height: number;
|
|
90
|
+
}
|
|
91
|
+
interface ResizeAspectRatioOptions {
|
|
92
|
+
ratio_width: number;
|
|
93
|
+
ratio_height: number;
|
|
94
|
+
}
|
|
95
|
+
interface ResizeLongestSideOptions {
|
|
96
|
+
longest_side: number;
|
|
97
|
+
}
|
|
98
|
+
type ResizeOptions = ResizeDimensionOptions | ResizeAspectRatioOptions | ResizeLongestSideOptions;
|
|
99
|
+
declare abstract class ImpageResizeOptions {
|
|
100
|
+
abstract get optionExport(): ResizeOptions;
|
|
101
|
+
}
|
|
102
|
+
declare class ImageResizeDimensionOptions extends ImpageResizeOptions {
|
|
103
|
+
width: number;
|
|
104
|
+
height: number;
|
|
105
|
+
constructor(payload: {
|
|
106
|
+
width: number;
|
|
107
|
+
height: number;
|
|
108
|
+
});
|
|
109
|
+
get optionExport(): ResizeDimensionOptions;
|
|
110
|
+
}
|
|
111
|
+
declare class ImageResizeAspectRatioOptions extends ImpageResizeOptions {
|
|
112
|
+
ratio_width: number;
|
|
113
|
+
ratio_height: number;
|
|
114
|
+
constructor(payload: {
|
|
115
|
+
ratio_width: number;
|
|
116
|
+
ratio_height: number;
|
|
117
|
+
});
|
|
118
|
+
get optionExport(): ResizeAspectRatioOptions;
|
|
119
|
+
}
|
|
120
|
+
declare class ImageResizeLongestSideOptions extends ImpageResizeOptions {
|
|
121
|
+
longest_side: number;
|
|
122
|
+
constructor(payload: {
|
|
123
|
+
longest_side: number;
|
|
124
|
+
});
|
|
125
|
+
get optionExport(): ResizeLongestSideOptions;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface ImageConverterInput {
|
|
129
|
+
compression?: ImageCompressionOptions;
|
|
130
|
+
resize?: ImpageResizeOptions;
|
|
131
|
+
crop?: ImageCropOptions;
|
|
132
|
+
}
|
|
133
|
+
declare class ImageConverter {
|
|
134
|
+
compression?: ImageCompressionOptions;
|
|
135
|
+
resize?: ImpageResizeOptions;
|
|
136
|
+
crop?: ImageCropOptions;
|
|
137
|
+
constructor(payload: ImageConverterInput);
|
|
138
|
+
get options(): Record<string, unknown>;
|
|
139
|
+
convertImageFile(file: File): Promise<Uint8Array<ArrayBufferLike>>;
|
|
140
|
+
convertImageBytes(bytes: Uint8Array): Promise<Uint8Array<ArrayBufferLike>>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
declare function getJpegConverter(payload?: {
|
|
144
|
+
quality?: number;
|
|
145
|
+
longest_side?: number;
|
|
146
|
+
}): ImageConverter;
|
|
147
|
+
|
|
148
|
+
declare function getPngConverter(payload?: {
|
|
149
|
+
compressionLevel?: number;
|
|
150
|
+
longest_side?: number;
|
|
151
|
+
}): ImageConverter;
|
|
152
|
+
|
|
153
|
+
export { BmpCompressionOptions, GifCompressionOptions, ImageConverter, ImageCropAspectRatioOptions, ImageCropDimensionOptions, ImageCropEachSideOptions, ImageResizeAspectRatioOptions, ImageResizeDimensionOptions, ImageResizeLongestSideOptions, JpegCompressionOptions, PngCompressionOptions, TgaCompressionOptions, TiffCompressionOptions, getJpegConverter, getPngConverter };
|
package/dist/index.js
CHANGED
|
@@ -451,17 +451,17 @@ var JpegCompressionOptions = class extends ImageCompressionOptions {
|
|
|
451
451
|
}
|
|
452
452
|
};
|
|
453
453
|
var PngCompressionOptions = class extends ImageCompressionOptions {
|
|
454
|
-
constructor(
|
|
454
|
+
constructor(quality) {
|
|
455
455
|
super();
|
|
456
|
-
if (
|
|
456
|
+
if (quality < 0 || quality > 9) {
|
|
457
457
|
throw new Error("Compression level must be between 0 and 9");
|
|
458
458
|
}
|
|
459
|
-
this.
|
|
459
|
+
this.quality = quality;
|
|
460
460
|
}
|
|
461
461
|
get optionExport() {
|
|
462
462
|
return {
|
|
463
463
|
format: "png",
|
|
464
|
-
quality: this.
|
|
464
|
+
quality: this.quality
|
|
465
465
|
};
|
|
466
466
|
}
|
|
467
467
|
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metools/web-image-converter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"dist/image_converter_back_end_bg.wasm"
|
|
9
|
-
],
|
|
10
7
|
"type": "module",
|
|
11
8
|
"sideEffects": false,
|
|
12
9
|
"author": "Mathew E. Thompson",
|
package/readme.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# @metools/web-image-converter
|
|
2
|
+
|
|
3
|
+
### An image-converter for use on the web, written in Rust.
|
|
4
|
+
|
|
5
|
+
Ever had a really large image that you wanted to compress quickly?
|
|
6
|
+
|
|
7
|
+
Do users upload enormous 20 megapixel photos to your website?
|
|
8
|
+
|
|
9
|
+
Do you want to cut down on file uploads and compute on your servers?
|
|
10
|
+
|
|
11
|
+
This package is for you!
|
|
12
|
+
|
|
13
|
+
@metools/web-image-converter is a web-compatible package for converting images. It has basic operations for resizing, cropping and compressing images. It can read from and convert to the following formats:
|
|
14
|
+
|
|
15
|
+
- Jpeg
|
|
16
|
+
- Png
|
|
17
|
+
- Gif
|
|
18
|
+
- Bmp
|
|
19
|
+
- Targa/Tga
|
|
20
|
+
- Tiff
|
|
21
|
+
|
|
22
|
+
The meat of the project is written in Rust and exported into a WebAssembly binary. The project also contains a TypeScript API for interacting with the WebAssembly back end.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
The project uses a set of classes for a declarative approach to converting images. The project is based around the ImageConverter class and several option classes that can be used.
|
|
27
|
+
|
|
28
|
+
The ImageConverter class can be used with either a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object or a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) representing the binary data of an image file.
|
|
29
|
+
|
|
30
|
+
Example usage:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
function convertMyImage(file: File) {
|
|
34
|
+
// Creates a basic image converter
|
|
35
|
+
const converter = new ImageConverter();
|
|
36
|
+
// Converts a JS File object and returns a Uint8Array
|
|
37
|
+
const result = await converter.convertImageFile(file);
|
|
38
|
+
// Afterward, you can do what you want with this byte array,
|
|
39
|
+
// such as streaming it to a server via a Form Post or make
|
|
40
|
+
// a Blob and download it with URL.createObjectURL
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`ImageConverter` provides two different ways to convert images:
|
|
46
|
+
|
|
47
|
+
- `convertImageFile(file: File): Promise<Uint8Array<ArrayBufferLike>>`
|
|
48
|
+
- This API accepts a JavaScript File object and runs it through the process
|
|
49
|
+
- `convertImageBytes(bytes: Uint8Array): Promise<Uint8Array<ArrayBufferLike>>`
|
|
50
|
+
- This API accepts a Uint8Array and does the same thing as `convertImageFile`.
|
|
51
|
+
|
|
52
|
+
Both APIs return a `Uint8Array` object that can be used in any place where you may want to stream or save files:
|
|
53
|
+
|
|
54
|
+
- [MDN Sending and Receiving Binary Data](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Sending_and_Receiving_Binary_Data)
|
|
55
|
+
- [MDN createObjectURL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static)
|
|
56
|
+
- [MDN Using object URLs to display images](https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications#example_using_object_urls_to_display_images)
|
|
57
|
+
|
|
58
|
+
### Options
|
|
59
|
+
|
|
60
|
+
You can specify one of several `CompressionOptions` objects to represent output format and compression:
|
|
61
|
+
|
|
62
|
+
- `JpegCompressionOptions(quality: number)`
|
|
63
|
+
- Quality represents JPEG image quality. Should be a number between 1-100
|
|
64
|
+
- `PngCompressionOptions(quality: number)`
|
|
65
|
+
- Quality represents PNG image quality. Quality above 75 uses `Best` compression and everything else uses `Fast` compression
|
|
66
|
+
- `GifCompressionOptions()`
|
|
67
|
+
- `BmpCompressionOptions()`
|
|
68
|
+
- `TgaCompressionOptions()`
|
|
69
|
+
- `TiffCompressionOptions()`
|
|
70
|
+
|
|
71
|
+
You can specify one of several `ResizeOptions` objects to represent resizing an image:
|
|
72
|
+
|
|
73
|
+
- `ImageResizeLongestSideOptions(payload: { longest_side: number; })`
|
|
74
|
+
- This operation will find the longest side and resize that side to the provided number. This operation will preserve the aspect ratio.
|
|
75
|
+
- `ImageResizeDimensionOptions(payload: { width: number; height: number; })`
|
|
76
|
+
- This will resize the image to the specified width and height. This operation will not preserve the image's aspect ratio and may cause squishing or stretching
|
|
77
|
+
- `ImageResizeAspectRatioOptions(payload: { ratio_width: number; ratio_height: number; })`
|
|
78
|
+
- This operation will resize the image to a specific aspect ratio. This operation may cause squishing or stretching
|
|
79
|
+
|
|
80
|
+
You can specify one of serveral `CropOptions` objects to represent cropping an image:
|
|
81
|
+
|
|
82
|
+
- `ImageCropDimensionOptions(payload: { x: number; y: number; width: number; height: number; })`
|
|
83
|
+
- This operation sets the x and y position, then defines the width and height of a new image. This operation allows you to defined a sub-picture within the larger picture.
|
|
84
|
+
- `ImageCropAspectRatioOptions(payload: { ratio_width: number; ratio_height: number; })`
|
|
85
|
+
- This operation allows you to define a new aspect ratio for an image and crops the image in the center based on this aspect ratio. E.g. you can use 1 for both `ratio_width` and `ratio_height` to define a square and crop a square in the middle of the image
|
|
86
|
+
- `ImageCropEachSideOptions(payload: { crop_left: number; crop_right: number; crop_top: number; crop_bottom: number; })`
|
|
87
|
+
- This operation allows you to describe cropping pixels on each of the four sides of the image.
|
|
88
|
+
|
|
89
|
+
### Errors
|
|
90
|
+
|
|
91
|
+
When the files and binary data provided conform to the file specs of the above supported image formats, the project should work fine. However, if the data provided is NOT an image, `convertImageFile` and `convertImageBytes` will each throw an error. The function should be wrapped in a try / catch block to catch errors.
|
|
92
|
+
|
|
93
|
+
### Blocking Operation
|
|
94
|
+
|
|
95
|
+
Running an operation via WebAssmebly is a blocking operation. This means that when the operation is called, it will block a wide variety of tasks in the application from continuing to run, such as UI operations, timers, etc. For instance, when compressing a large image from a DSLR camera, it may take several seconds for the operation to complete and during that time everything else freezes.
|
|
96
|
+
|
|
97
|
+
As such, web workers may be a beneficial to put this operation into a web worker for the sake of leaving the main thread open to other operations:
|
|
98
|
+
|
|
99
|
+
[MDN Using Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)
|
|
100
|
+
|
|
101
|
+
## Examples
|
|
102
|
+
|
|
103
|
+
Converting a file to Jpeg:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
function makeMyFileAJpeg(file: File) {
|
|
107
|
+
// Jpeg compression is set to 60. Decent middle ground for
|
|
108
|
+
// quality and size
|
|
109
|
+
const converter = new ImageConverter({
|
|
110
|
+
compression: new JpegCompressionOptions(60),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const result = await converter.convertImageFile(file);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Creating a PNG file:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
function makeMyFileAPng(file: File) {
|
|
121
|
+
// Jpeg compression is set to 60. Decent middle ground for
|
|
122
|
+
// quality and size
|
|
123
|
+
const converter = new ImageConverter({
|
|
124
|
+
compression: new PngCompressionOptions(60),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = await converter.convertImageFile(file);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Using several converters to make several different files:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
function makeImageSet(bin: Uint8Array) {
|
|
135
|
+
const thumb = new ImageConverter({
|
|
136
|
+
compression: new JpegCompressionOptions(40),
|
|
137
|
+
resize: new ImageResizeLongestSideOptions({
|
|
138
|
+
longest_side: 128,
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
const preview = new ImageConverter({
|
|
142
|
+
compression: new JpegCompressionOptions(40),
|
|
143
|
+
resize: new ImageResizeLongestSideOptions({
|
|
144
|
+
longest_side: 256,
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
const image = new ImageConverter({
|
|
148
|
+
compression: new JpegCompressionOptions(60),
|
|
149
|
+
resize: new ImageResizeLongestSideOptions({
|
|
150
|
+
longest_side: 1280,
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const [thumbResult, previewResult, imageResult] = Promise.all([
|
|
155
|
+
thumb.convertImageBytes(bin),
|
|
156
|
+
preview.convertImageBytes(bin),
|
|
157
|
+
image.convertImageBytes(bin),
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
// Do what you want
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Cropping an image to a square and resizing for a profile pic
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
function makeProfilePic(file: File) {
|
|
168
|
+
const profile = new ImageConverter({
|
|
169
|
+
compression: new JpegCompressionOptions(40),
|
|
170
|
+
crop: new ImageCropAspectRatioOptions({
|
|
171
|
+
ratio_width: 1,
|
|
172
|
+
ratio_height: 1,
|
|
173
|
+
}),
|
|
174
|
+
resize: new ImageResizeLongestSideOptions({
|
|
175
|
+
longest_side: 128,
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const result = await profile.convertImageFile(file);
|
|
180
|
+
}
|
|
181
|
+
```
|