@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.
@@ -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(compressionLevel) {
454
+ constructor(quality) {
455
455
  super();
456
- if (compressionLevel < 0 || compressionLevel > 9) {
456
+ if (quality < 0 || quality > 9) {
457
457
  throw new Error("Compression level must be between 0 and 9");
458
458
  }
459
- this.compressionLevel = compressionLevel;
459
+ this.quality = quality;
460
460
  }
461
461
  get optionExport() {
462
462
  return {
463
463
  format: "png",
464
- quality: this.compressionLevel
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.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
+ ```