@mutables.app/image-optimizer 0.3.0 → 0.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Standalone image optimizer
2
2
 
3
- Rust/Emscripten JPEG, PNG, HEIF/HEIC and AVIF decoding, scanline Lanczos3 resizing with WASM SIMD128, and WebP/JPEG output. No Cloudflare Computer dependency.
3
+ Rust/Emscripten JPEG, non-interlaced PNG, and tiled/grid HEIC/HEIF decoding, scanline Lanczos3 resizing with WASM SIMD128, and WebP/JPEG output. Adam7/interlaced PNG, non-grid HEIC/HEIF, and AVIF input are rejected. HEIC/HEIF grid decoding uses libde265. No Cloudflare Computer dependency.
4
4
 
5
5
  ```js
6
6
  import { image } from '@mutables.app/image-optimizer';
@@ -14,13 +14,45 @@ const jpeg = await image(inputBytes).resize(800).jpeg({quality: 80}).bytes();
14
14
 
15
15
  Input: Blob, ArrayBuffer, or typed array. `.bytes()` returns a Uint8Array; `.blob()` returns an image/webp or image/jpeg Blob. Width-only resizing preserves aspect ratio. Two dimensions default to stretch; `fit: 'inside'` preserves aspect ratio.
16
16
 
17
- **Scanline resizing applies with and without `speed: 'fast'`.** Baseline JPEG and non-interlaced PNG inputs are read row by row. Non-grid HEIF/HEIC and AVIF inputs are decoded by libheif into a complete RGBA source image before rows enter the resizer and remain capped at 8 million pixels. Grid/tiled HEIF inputs decode one tile row at a time and support up to 64 million pixels. WebP retains the complete resized image for encoding. JPEG encodes resized rows directly, uses baseline 4:2:0 output and composites transparency onto white. Input and encoded output bytes remain buffered: this API does not stream HTTP input/output.
17
+ **Scanline resizing applies with and without `speed: 'fast'`.** Baseline JPEG and non-interlaced PNG inputs are read row by row. Tiled/grid HEIF/HEIC inputs decode one tile row at a time and support up to 48 million pixels. Adam7/interlaced PNG and non-grid HEIF/HEIC inputs are rejected; AVIF input is also unsupported. WebP retains the complete resized image for encoding. JPEG encodes resized rows directly, uses baseline 4:2:0 output and composites transparency onto white. Input and encoded output bytes remain buffered: this API does not stream HTTP input/output.
18
18
 
19
- Supported progressive JPEGs use lowzag's reduced coefficient storage and decode to a power-of-two scale before row resizing. Unsupported progressive and multi-scan JPEGs fall back to libjpeg coefficient buffering; Adam7 PNG uses a bounded complete-image decode. These fallback formats have higher memory requirements. No fixed whole-Worker memory guarantee is made.
19
+ Only Lowzag-compatible progressive JPEGs use reduced coefficient storage and decode to a power-of-two scale before row resizing. Other progressive/multi-scan variants, including CMYK and YCCK, are rejected before coefficient buffering. No fixed whole-Worker memory guarantee is made.
20
20
 
21
21
  For WebP, `speed: 'fast'` selects a faster internal encoder setting and enables scaled JPEG decoding where beneficial. The fast encoder can produce larger files. For JPEG output, `speed: 'fast'` enables scaled JPEG decoding; the JPEG encoder uses the same settings in both modes. Omit speed for full-resolution decoding and the default WebP encoder. `.webp({lossless: true})` uses lossless WebP encoding and disables scaled decoding; resizing still changes pixels.
22
22
 
23
- 20 MiB input limit; lowzag-compatible progressive JPEGs support up to 64 million source pixels, baseline JPEG and non-interlaced PNG up to 32 million, and unsupported progressive/multi-scan JPEGs and Adam7 PNGs up to 24 million. Non-grid HEIF/HEIC and AVIF are capped at 8 million; tiled HEIF grids are capped at 64 million. `image(input,{maxPixels: ...})` bounds output pixels: 8 million by default, maximum 16 million. Dimensions are limited to 16383. The WASM build allows up to 256 MiB of linear memory; Cloudflare Worker isolates are limited to 128 MiB total across JavaScript and WebAssembly. Image processing runs synchronously inside the terminal method's promise.
23
+ ## Input and output limits
24
+
25
+ | Input type | Maximum source pixels | Decode path | WASM linear-memory capacity | Cloudflare isolate memory (max sampled) |
26
+ | --- | ---: | --- | ---: | ---: |
27
+ | Baseline JPEG | 48 MP | Sequential scanline decode; eligible resized JPEGs can use scaled decode with `speed: 'fast'`. | 16 MiB | 36.14 MiB |
28
+ | Non-interlaced PNG | 48 MP | Sequential scanline decode. | 16 MiB | 21.17 MiB |
29
+ | Lowzag-compatible progressive JPEG | 48 MP | Reduced coefficient decode when resizing; coefficient planes are checked before any full-resolution fallback. | 27.75 MiB | 43.23 MiB |
30
+ | Tiled/grid HEIC/HEIF | 48 MP | libde265 (HEVC); decodes one tile row at a time. | 49 MiB | 54.97 MiB |
31
+
32
+ ### Worker response time and CPU
33
+
34
+ These end-to-end samples convert supported synthetic inputs to 800×600 WebP. Each fixture ran in its own temporary Worker with one warm-up request followed by three measured requests. Response time is client-observed p50 and includes network and transfer time; CPU is the median per-invocation `cpuTime` reported by Wrangler tail. Wrangler captured one to three CPU samples per fixture; the sample count appears beside each CPU median. The temporary Workers used a 30-second CPU limit.
35
+
36
+ | Input path and source size | Result | Response p50 | Worker CPU median | WASM capacity after last request |
37
+ | --- | --- | ---: | ---: | ---: |
38
+ | Baseline JPEG, 12 MP | Converted | 234 ms | 111 ms (1 sample) | 16 MiB |
39
+ | Baseline JPEG, 24 MP | Converted | 367 ms | 198 ms (1 sample) | 16 MiB |
40
+ | Baseline JPEG, 48 MP | Converted | 630 ms | 346 ms (3 samples) | 16 MiB |
41
+ | Non-interlaced PNG, 12 MP | Converted | 469 ms | 381 ms (3 samples) | 16 MiB |
42
+ | Non-interlaced PNG, 24 MP | Converted | 547 ms | 426 ms (3 samples) | 16 MiB |
43
+ | Non-interlaced PNG, 48 MP | Converted | 1,047 ms | 932 ms (3 samples) | 16 MiB |
44
+ | Lowzag progressive JPEG, 12 MP | Converted | 671 ms | 438 ms (3 samples) | 16 MiB |
45
+ | Lowzag progressive JPEG, 24 MP | Converted | 708 ms | 498 ms (3 samples) | 27.75 MiB |
46
+ | Lowzag progressive JPEG, 48 MP | Converted | 1,359 ms | 1,032 ms (3 samples) | 27.75 MiB |
47
+ | Tiled/grid HEIC/HEIF, 12 MP | Converted | 962 ms | 710 ms (3 samples) | 16 MiB |
48
+ | Tiled/grid HEIC/HEIF, 24 MP | Converted | 885 ms | 734 ms (3 samples) | 36.88 MiB |
49
+ | Tiled/grid HEIC/HEIF, 48 MP | Converted | 1,570 ms | 1,417 ms (3 samples) | 48.63 MiB |
50
+
51
+ See the [raw Worker performance results](../../benchmarks/decoder-performance-worker.json). Adam7 PNG, non-grid HEIC/HEIF, and non-Lowzag progressive JPEG measurements are retained under `historicalUnsupportedResults` and do not describe current support. These small synthetic-fixture samples show this test run, not guaranteed production latency.
52
+
53
+ All formats share a 20 MiB input limit and a 16,383-pixel dimension limit. `image(input,{maxPixels: ...})` bounds output pixels: 8 MP by default, maximum 16 MP. Adam7/interlaced PNG and non-grid HEIC/HEIF are unsupported. Only Lowzag-compatible progressive JPEGs are accepted; all other progressive/multi-scan variants, including CMYK and YCCK, are rejected. WASM capacity is not total Worker memory. Cloudflare's per-invocation isolate-memory metric includes shared JavaScript and WebAssembly memory, but is sampled at invocation time rather than measuring exact peak memory. Cloudflare's isolate limit is 128 MiB. Image processing runs synchronously inside the terminal method's promise.
54
+
55
+ Worker measurements used synthetic fixtures and 800×600 WebP output. A fresh Worker handled each fixture with a 30-second CPU ceiling; the normal experiment Worker uses 1,000 ms and may time out on large inputs. Cloudflare `memoryUsageBytesP99` samples are observed isolate-memory measurements, not exact peaks. Historical measurements for now-unsupported progressive JPEG variants remain under `historicalUnsupportedResults` in the [full Worker measurement report](../../benchmarks/decoder-memory-worker.json).
24
56
 
25
57
  Node uses a file-based WASM loader; Workers use a static `.wasm` import. Configure your bundler to treat that file as a WebAssembly module. Requires WebAssembly SIMD and modern WASM exception handling. Workers tested with compatibility date 2026-09-02 and `new_module_registry`.
26
58
 
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutables.app/image-optimizer",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,26 +0,0 @@
1
- Copyright (c) 2016, Alliance for Open Media. All rights reserved.
2
-
3
- Redistribution and use in source and binary forms, with or without
4
- modification, are permitted provided that the following conditions
5
- are met:
6
-
7
- 1. Redistributions of source code must retain the above copyright
8
- notice, this list of conditions and the following disclaimer.
9
-
10
- 2. Redistributions in binary form must reproduce the above copyright
11
- notice, this list of conditions and the following disclaimer in
12
- the documentation and/or other materials provided with the
13
- distribution.
14
-
15
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18
- FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19
- COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25
- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
- POSSIBILITY OF SUCH DAMAGE.