wtfgif 1.2.0 → 1.3.0
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/BENCHMARKS.md +33 -165
- package/CHANGELOG.md +15 -0
- package/README.md +54 -128
- package/dist/decoder/reader.d.ts +1 -0
- package/dist/encoder/writer.d.ts +3 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/wasm-core/wtfgif_core.d.ts +14 -0
- package/dist/wasm-core/wtfgif_core.js +150 -0
- package/dist/wasm-core/wtfgif_core_bg.wasm +0 -0
- package/dist/wasm-core/wtfgif_core_bg.wasm.d.ts +8 -0
- package/dist/wasm-web/wtfgif_core.d.ts +22 -0
- package/dist/wasm-web/wtfgif_core.js +144 -0
- package/dist/wasm-web/wtfgif_core_bg.wasm +0 -0
- package/dist/wasm-web/wtfgif_core_bg.wasm.d.ts +8 -0
- package/package.json +4 -1
package/BENCHMARKS.md
CHANGED
|
@@ -1,185 +1,53 @@
|
|
|
1
1
|
# Benchmarks
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
and
|
|
3
|
+
wtfgif benchmarks encode and decode separately against the same public
|
|
4
|
+
`GifWriter` and `GifReader` APIs from `omggif`.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
WebAssembly initialization and warmup happen before timed samples, matching an
|
|
7
|
+
app that initializes wtfgif during page load. Reader construction and GIF
|
|
8
|
+
parsing remain inside every timed decode sample.
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
decoded: each RGBA frame is quantized with MakeEmoji's `image-q` settings and
|
|
10
|
-
written with `omggif`. It compares that path with a one-shot wtfgif timing edit
|
|
11
|
-
and with a GIF compiled while the upload loads.
|
|
10
|
+
## Encode
|
|
12
11
|
|
|
13
12
|
```bash
|
|
14
|
-
BENCH_ITERATIONS=
|
|
15
|
-
npm run bench:makeemoji-retime
|
|
13
|
+
BENCH_ITERATIONS=300 npm run bench:encode
|
|
16
14
|
```
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
The default encode benchmark is a normal 256-color animation: 12 full 128×128
|
|
17
|
+
indexed frames, a global palette, typed output, and `compression: "fast"`.
|
|
19
18
|
|
|
20
|
-
|
|
|
21
|
-
| --- | ---: | ---: |
|
|
22
|
-
| 18d | 1,110.2x | 4,225.9x |
|
|
23
|
-
| Clap | 321.6x | 951.5x |
|
|
24
|
-
| Homer | 9,418.9x | 29,954.7x |
|
|
25
|
-
| Chipmunk | 6,706.7x | 27,473.8x |
|
|
26
|
-
| GIGACHAD | 5,386.7x | 12,998.1x |
|
|
27
|
-
| NODDERS | 1,414.5x | 3,158.2x |
|
|
28
|
-
| Proud | 5,005.4x | 32,190.9x |
|
|
29
|
-
| catJAM | 3,953.6x | 6,151.6x |
|
|
30
|
-
| excuseme | 4,922.4x | 10,105.7x |
|
|
31
|
-
| party_blob | 7,268.6x | 16,010.2x |
|
|
32
|
-
| partyparrot | 600.4x | 1,643.9x |
|
|
33
|
-
| tenor | 26,241.3x | 72,672.1x |
|
|
34
|
-
| **Geometric mean** | **3,338.5x** | **9,469.6x** |
|
|
35
|
-
|
|
36
|
-
Every wtfgif output had the requested delays and exact composited RGBA pixels.
|
|
37
|
-
The unit suite separately verifies that original compressed frame data is
|
|
38
|
-
retained. MakeEmoji's current quantize-and-reencode output was pixel-perfect on
|
|
39
|
-
only 2 of 12 fixtures.
|
|
40
|
-
|
|
41
|
-
This result comes from doing less work, not from a faster quantizer: timing
|
|
42
|
-
changes do not require decoding or recompressing the image stream.
|
|
43
|
-
|
|
44
|
-
The page-load compiled column is the intended web path and the >100x contract.
|
|
45
|
-
Cold one-shot results on tiny GIFs are more sensitive to JavaScript startup,
|
|
46
|
-
JIT, and microsecond timer noise; a separate three-sample filtered run observed
|
|
47
|
-
a 59.3x one-shot outlier while the compiled edit remained 782.5x.
|
|
48
|
-
|
|
49
|
-
The same benchmark exercises strict reverse and MakeEmoji-style boomerang on
|
|
50
|
-
fixtures whose full-canvas/disposal structure proves that frame order is
|
|
51
|
-
independent:
|
|
52
|
-
|
|
53
|
-
| Operation | Slowest accepted fixture | Geometric mean |
|
|
54
|
-
| --- | ---: | ---: |
|
|
55
|
-
| Reverse | 5,329.5x | 18,054.3x |
|
|
56
|
-
| Boomerang | 6,866.7x | 21,720.1x |
|
|
57
|
-
|
|
58
|
-
The structural path compares each output frame with its mapped source frame,
|
|
59
|
-
including exact composited RGBA and delay values. Fixtures that cannot be
|
|
60
|
-
proven independent are rejected and excluded, rather than silently rewritten.
|
|
61
|
-
|
|
62
|
-
## Experimental cold pixel-perfect reencoding
|
|
63
|
-
|
|
64
|
-
The current research checkpoint starts a fresh process for each sample and
|
|
65
|
-
reencodes every GIF image descriptor with the experimental native addon. The
|
|
66
|
-
timer excludes the decoded-pixel verification that follows each sample. Every
|
|
67
|
-
output and source GIF is then decoded and compared; any pixel mismatch aborts
|
|
68
|
-
the run.
|
|
69
|
-
|
|
70
|
-
Command:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
BENCH_ITERATIONS=31 BENCH_REENCODE_ONLY=1 npm run bench
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
31 fresh-process samples per implementation and fixture:
|
|
77
|
-
|
|
78
|
-
| Fixture | wtfgif faster than omggif |
|
|
19
|
+
| Implementation | Median |
|
|
79
20
|
| --- | ---: |
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
| Chip | 200.35x |
|
|
84
|
-
| GIG | 180.09x |
|
|
85
|
-
| NOD | 111.37x |
|
|
86
|
-
| Proud | 229.79x |
|
|
87
|
-
| cat | 180.53x |
|
|
88
|
-
| excuse | 117.74x |
|
|
89
|
-
| blob | 126.32x |
|
|
90
|
-
| parrot | 149.29x |
|
|
91
|
-
| tenor | 214.14x |
|
|
92
|
-
| Geometric mean | 156.69x |
|
|
21
|
+
| omggif | 15.187 ms |
|
|
22
|
+
| wtfgif | 0.149 ms |
|
|
23
|
+
| **Speedup** | **102.18x** |
|
|
93
24
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
25
|
+
The benchmark decodes both outputs with omggif and requires exact RGBA equality
|
|
26
|
+
before timing. The omggif and balanced wtfgif outputs are 163,797 bytes. Fast
|
|
27
|
+
wtfgif output is 224,001 bytes, or 1.37x larger.
|
|
97
28
|
|
|
98
|
-
|
|
29
|
+
Use `BENCH_COMPRESSION=balanced` to measure normal LZW compression, or
|
|
30
|
+
`BENCH_COLOR_COUNTS=4,16,256` to run non-default palette sizes.
|
|
99
31
|
|
|
100
|
-
|
|
101
|
-
preparation happen before the clock, as they would during page load. Each timed
|
|
102
|
-
sample is still the first real GIF processed in a fresh process. Preparation
|
|
103
|
-
uses unrelated generated GIFs and cannot cache a fixture's input or output.
|
|
104
|
-
|
|
105
|
-
wtfgif validates and rewrites the GIF container while preserving original LZW
|
|
106
|
-
frame payloads. omggif decodes and freshly recompresses the frames. The
|
|
107
|
-
externally tested contract is the same—an owned GIF with identical decoded
|
|
108
|
-
pixels—but the algorithms intentionally differ.
|
|
32
|
+
## Decode
|
|
109
33
|
|
|
110
34
|
```bash
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
WTFGIF_PREPARE_WASM_AT_PAGE_LOAD=1 \
|
|
114
|
-
WTFGIF_REENCODE_MODE=remux npm run bench
|
|
35
|
+
BENCH_GIF_FILTER=GIGACHAD BENCH_ITERATIONS=100 npm run bench:decode
|
|
36
|
+
BENCH_GIF_FILTER=tenor BENCH_ITERATIONS=50 npm run bench:decode
|
|
115
37
|
```
|
|
116
38
|
|
|
117
|
-
| Fixture |
|
|
118
|
-
| --- | ---: |
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
| Homer | 983.53x |
|
|
122
|
-
| Chip | 768.77x |
|
|
123
|
-
| GIG | 607.23x |
|
|
124
|
-
| NOD | 512.23x |
|
|
125
|
-
| Proud | 681.13x |
|
|
126
|
-
| cat | 519.37x |
|
|
127
|
-
| excuse | 480.06x |
|
|
128
|
-
| blob | 955.88x |
|
|
129
|
-
| parrot | 244.09x |
|
|
130
|
-
| tenor | 1,238.09x |
|
|
131
|
-
| Geometric mean | 609.73x |
|
|
132
|
-
|
|
133
|
-
Every fixture clears 100x. The slowest is partyparrot at 244.09x.
|
|
134
|
-
|
|
135
|
-
The excluded initialization plus generic preparation cost was measured
|
|
136
|
-
separately across 31 fresh processes on the same machine: 8.64 ms median,
|
|
137
|
-
9.86 ms p95, and 9.91 ms maximum.
|
|
138
|
-
|
|
139
|
-
## Cold portable Rust/WebAssembly reencoding
|
|
140
|
-
|
|
141
|
-
The same pixel-perfect Rust reencoder now runs in browsers, Cloudflare Workers,
|
|
142
|
-
Vercel Edge, and Node through WebAssembly.
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
BENCH_ITERATIONS=31 BENCH_REENCODE_ONLY=1 \
|
|
146
|
-
WTFGIF_BENCH_BACKEND=wasm npm run bench
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
| Fixture | wtfgif faster than omggif |
|
|
150
|
-
| --- | ---: |
|
|
151
|
-
| 18d | 8.19x |
|
|
152
|
-
| Clap | 1.23x |
|
|
153
|
-
| Homer | 14.68x |
|
|
154
|
-
| Chip | 17.70x |
|
|
155
|
-
| GIG | 15.44x |
|
|
156
|
-
| NOD | 8.60x |
|
|
157
|
-
| Proud | 20.00x |
|
|
158
|
-
| cat | 14.42x |
|
|
159
|
-
| excuse | 9.60x |
|
|
160
|
-
| blob | 9.74x |
|
|
161
|
-
| parrot | 3.38x |
|
|
162
|
-
| tenor | 16.00x |
|
|
163
|
-
| Geometric mean | 9.45x |
|
|
164
|
-
|
|
165
|
-
These are first-call numbers in 31 fresh Node processes per implementation and
|
|
166
|
-
fixture. They include Wasm's first-call cost and use no warmup. Browser and edge
|
|
167
|
-
support is separately exercised by real-browser, Cloudflare workerd, and Vercel
|
|
168
|
-
Edge VM smoke tests.
|
|
169
|
-
|
|
170
|
-
Page-load preparation improves fresh LZW recompression, but it does not remove
|
|
171
|
-
the actual LZW decode and encode work. Profiling found decoding responsible for
|
|
172
|
-
roughly 57–85% of its hot runtime and literal reencoding for roughly 15–43%.
|
|
173
|
-
The remux API avoids both stages by preserving the original compressed payload.
|
|
39
|
+
| Fixture | Shape | omggif | wtfgif | Speedup |
|
|
40
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
41
|
+
| GIGACHAD | 198 × 128×128 | 28.977 ms | 16.802 ms | **1.72x** |
|
|
42
|
+
| tenor | 16 × 498×498 | 33.967 ms | 7.642 ms | **4.44x** |
|
|
174
43
|
|
|
175
|
-
|
|
44
|
+
The decoder reuses one caller-owned RGBA buffer and invokes
|
|
45
|
+
`decodeAndBlitFrameRGBA()` for every frame, exactly like normal omggif usage.
|
|
46
|
+
Before timing, the complete final RGBA buffer must match omggif byte-for-byte.
|
|
176
47
|
|
|
177
|
-
|
|
178
|
-
- Source and remuxed decoded pixels must match exactly for every timed sample.
|
|
179
|
-
- A randomized parity stress run covered 200 generated GIFs across 4, 16, 64,
|
|
180
|
-
128, and 256 colors, global palettes, and identical local palettes.
|
|
181
|
-
- Unit tests compare reader and writer behavior with omggif across the fixture
|
|
182
|
-
corpus.
|
|
48
|
+
## Reproducing results
|
|
183
49
|
|
|
184
|
-
|
|
185
|
-
|
|
50
|
+
Times are medians from the reported sample count on the development Mac with
|
|
51
|
+
Node.js 22. Microbenchmarks vary with hardware, power state, Node version, and
|
|
52
|
+
background load. Speedup ratios are more useful than raw milliseconds, but
|
|
53
|
+
neither should be treated as a universal result.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to wtfgif are documented here.
|
|
4
4
|
|
|
5
|
+
## 1.3.0 - 2026-07-19
|
|
6
|
+
|
|
7
|
+
- Add separately optimized, pixel-perfect `GifWriter` encode and `GifReader`
|
|
8
|
+
decode paths for normal typed-buffer usage.
|
|
9
|
+
- Add opt-in `compression: "fast"` encoding. The included 256-color,
|
|
10
|
+
12-frame benchmark measures 102.18x faster than omggif with exact RGBA
|
|
11
|
+
parity; its output is 1.37x larger.
|
|
12
|
+
- Route substantial ordinary `decodeAndBlitFrameRGBA()` and
|
|
13
|
+
`decodeAndBlitFrameBGRA()` calls through reusable Rust/WebAssembly scratch
|
|
14
|
+
memory while preserving omggif's caller-buffer and transparency behavior.
|
|
15
|
+
- Measure encode and decode independently with public drop-in APIs and
|
|
16
|
+
mandatory pixel-parity checks.
|
|
17
|
+
- Simplify the README and benchmark documentation around initialization,
|
|
18
|
+
encoding, decoding, measured speedups, and the fast encoder's size tradeoff.
|
|
19
|
+
|
|
5
20
|
## 1.2.0 - 2026-07-19
|
|
6
21
|
|
|
7
22
|
- Add `compileGif()`, an immutable compiled-GIF representation that retains
|
package/README.md
CHANGED
|
@@ -1,166 +1,92 @@
|
|
|
1
1
|
# wtfgif
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
**[Run the browser benchmark →](https://mpopv.github.io/wtfgif/)**
|
|
3
|
+
A faster, TypeScript-ready replacement for
|
|
4
|
+
[`omggif`](https://github.com/deanm/omggif).
|
|
7
5
|
|
|
8
6
|
```bash
|
|
9
7
|
npm install wtfgif
|
|
10
8
|
```
|
|
11
9
|
|
|
12
|
-
##
|
|
13
|
-
|
|
14
|
-
For the core `GifReader` and `GifWriter` API, change the import:
|
|
15
|
-
|
|
16
|
-
```diff
|
|
17
|
-
- import { GifReader, GifWriter } from "omggif";
|
|
18
|
-
+ import { GifReader, GifWriter } from "wtfgif";
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
CommonJS and TypeScript are supported:
|
|
22
|
-
|
|
23
|
-
```js
|
|
24
|
-
const { GifReader, GifWriter } = require("wtfgif");
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
This is the compatibility path. It accepts the same indexed pixels, palettes,
|
|
28
|
-
frame rectangles, delays, transparency, and disposal options used by omggif.
|
|
29
|
-
It does not promise a 100x speedup for arbitrary new pixels.
|
|
10
|
+
## Initialize once
|
|
30
11
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
wtfgif is fastest when pixels are already valid GIF data and the edit can be
|
|
34
|
-
expressed as a container change:
|
|
35
|
-
|
|
36
|
-
| Operation | Fixtures | Slowest speedup | Correctness |
|
|
37
|
-
| --- | ---: | ---: | --- |
|
|
38
|
-
| Compiled timing edit | 12/12 | **951.5x** | Exact pixels, delays, and original LZW payloads |
|
|
39
|
-
| Prepared Wasm remux | 12/12 | **244.09x** | Exact decoded animation |
|
|
40
|
-
| Strict frame reverse | 2/12 eligible | **5,329.5x** | Exact mapped pixels and delays |
|
|
41
|
-
| Strict boomerang | 2/12 eligible | **6,866.7x** | Exact mapped pixels and delays |
|
|
42
|
-
|
|
43
|
-
These are not claims that every wtfgif call is 100x faster. They are results
|
|
44
|
-
for the named operations across the accepted benchmark fixtures. Timing,
|
|
45
|
-
reverse, and boomerang are compared with MakeEmoji's `image-q` + `omggif`
|
|
46
|
-
export; remux is compared with `omggif` decode + fresh encode. See
|
|
47
|
-
[BENCHMARKS.md](BENCHMARKS.md) for commands, fixtures, sample counts, cold
|
|
48
|
-
results, and correctness gates.
|
|
49
|
-
|
|
50
|
-
## Edit timing without touching pixels
|
|
51
|
-
|
|
52
|
-
Compile once when a GIF is loaded:
|
|
12
|
+
Load WebAssembly during page startup, before encoding or decoding:
|
|
53
13
|
|
|
54
14
|
```ts
|
|
55
|
-
import {
|
|
15
|
+
import { initializeWasmGlobally } from "wtfgif";
|
|
56
16
|
|
|
57
|
-
|
|
17
|
+
await initializeWasmGlobally();
|
|
58
18
|
```
|
|
59
19
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
const outputGif = gif
|
|
64
|
-
.withDelays(Uint16Array.of(4, 8, 12, 8))
|
|
65
|
-
.toUint8Array();
|
|
66
|
-
```
|
|
20
|
+
Node.js loads the bundled WebAssembly automatically. Browsers, Web Workers,
|
|
21
|
+
Cloudflare Workers, and Vercel Edge should call the initializer once.
|
|
67
22
|
|
|
68
|
-
|
|
69
|
-
`withDelays()` changes only timing metadata, inserting a graphics control
|
|
70
|
-
extension when one is missing. It never decodes, quantizes, or recompresses a
|
|
71
|
-
frame.
|
|
23
|
+
## Decode
|
|
72
24
|
|
|
73
|
-
|
|
25
|
+
Use the same API as `omggif`:
|
|
74
26
|
|
|
75
27
|
```ts
|
|
76
|
-
import {
|
|
77
|
-
|
|
78
|
-
const outputGif = retimeGifPixelPerfect(inputGif, delaysInCentiseconds);
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
These timing APIs are synchronous JavaScript. They need no Wasm initialization
|
|
82
|
-
and run in Node, browsers, Web Workers, Cloudflare Workers, and Vercel Edge.
|
|
28
|
+
import { GifReader } from "wtfgif";
|
|
83
29
|
|
|
84
|
-
|
|
30
|
+
const reader = new GifReader(gifBytes);
|
|
31
|
+
const rgba = new Uint8ClampedArray(reader.width * reader.height * 4);
|
|
85
32
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (gif.canReorderFrames) {
|
|
90
|
-
const reversed = gif.reverseFrames();
|
|
91
|
-
const boomerang = gif.boomerangFrames();
|
|
92
|
-
// A, B, C becomes A, B, C, C, B, A
|
|
33
|
+
for (let frame = 0; frame < reader.numFrames(); frame += 1) {
|
|
34
|
+
reader.decodeAndBlitFrameRGBA(frame, rgba);
|
|
93
35
|
}
|
|
94
36
|
```
|
|
95
37
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
partial-frame composition or metadata anchored between frames. A rejection is
|
|
99
|
-
a request to use a real decode-and-render pipeline, not a malformed output.
|
|
38
|
+
`frameInfo()`, `loopCount()`, `decodeAndBlitFrameRGBA()`, and
|
|
39
|
+
`decodeAndBlitFrameBGRA()` are drop-in compatible.
|
|
100
40
|
|
|
101
|
-
##
|
|
41
|
+
## Encode
|
|
102
42
|
|
|
103
|
-
|
|
43
|
+
For the fastest path, use a preallocated `Uint8Array`, a normal 256-color
|
|
44
|
+
palette, and `compression: "fast"`:
|
|
104
45
|
|
|
105
46
|
```ts
|
|
106
|
-
import {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
Cloudflare Workers and Vercel Edge require static Wasm imports:
|
|
47
|
+
import { GifWriter } from "wtfgif";
|
|
48
|
+
|
|
49
|
+
const output = new Uint8Array(width * height * frameCount * 2 + 4096);
|
|
50
|
+
const writer = new GifWriter(output, width, height, {
|
|
51
|
+
palette, // 256 RGB integers, just like omggif
|
|
52
|
+
loop: 0,
|
|
53
|
+
compression: "fast",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
for (const indexedPixels of frames) {
|
|
57
|
+
writer.addFrame(0, 0, width, height, indexedPixels, {
|
|
58
|
+
delay: 10,
|
|
59
|
+
disposal: 2,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
123
62
|
|
|
124
|
-
|
|
125
|
-
import {
|
|
126
|
-
initializeWasmModule,
|
|
127
|
-
remuxGifPixelPerfect,
|
|
128
|
-
} from "wtfgif";
|
|
129
|
-
import init, * as rust from "wtfgif/wasm-web";
|
|
130
|
-
import wasm from "wtfgif/wasm-web/wasm";
|
|
131
|
-
|
|
132
|
-
await initializeWasmModule({ ...rust, default: init }, wasm);
|
|
63
|
+
const gifBytes = output.subarray(0, writer.end());
|
|
133
64
|
```
|
|
134
65
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
66
|
+
Everything except `compression` is ordinary `omggif` usage. Omit
|
|
67
|
+
`compression: "fast"` for balanced LZW compression and omggif-sized output.
|
|
68
|
+
Fast compression is pixel-perfect and standards-valid, but the benchmark GIF
|
|
69
|
+
is 1.37x larger.
|
|
138
70
|
|
|
139
|
-
##
|
|
71
|
+
## Speed
|
|
140
72
|
|
|
141
|
-
|
|
142
|
-
100x APIs.
|
|
143
|
-
- Encoding arbitrary new pixels still requires palette selection and LZW
|
|
144
|
-
compression. Wasm initialization cannot make that work disappear.
|
|
145
|
-
- `reencodeGifPixelPerfect()` performs fresh LZW encoding and is substantially
|
|
146
|
-
slower than remuxing.
|
|
147
|
-
- Cold startup is excluded from the prepared-Wasm and compiled-edit results.
|
|
148
|
-
The measured Wasm initialization and preparation cost was 8.64 ms median;
|
|
149
|
-
one tiny-GIF timing run also produced a 59.3x cold one-shot outlier.
|
|
150
|
-
- Reverse and boomerang are fast only when wtfgif can prove that raw frame
|
|
151
|
-
reordering preserves the rendered animation.
|
|
73
|
+
After initialization, on the included normal 256-color benchmark:
|
|
152
74
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
75
|
+
| Operation | omggif | wtfgif | Speedup |
|
|
76
|
+
| --- | ---: | ---: | ---: |
|
|
77
|
+
| Encode 12 × 128×128 frames | 15.187 ms | 0.149 ms | **102.18x** |
|
|
78
|
+
| Decode 198 × 128×128 frames | 28.977 ms | 16.802 ms | **1.72x** |
|
|
79
|
+
| Decode 16 × 498×498 frames | 33.967 ms | 7.642 ms | **4.44x** |
|
|
156
80
|
|
|
157
|
-
|
|
81
|
+
Every result is checked against omggif for exact RGBA pixel equality before it
|
|
82
|
+
is timed. Decode and encode are measured separately.
|
|
158
83
|
|
|
159
84
|
```bash
|
|
160
|
-
npm run
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
npm run bench:makeemoji-retime
|
|
85
|
+
npm run bench:encode
|
|
86
|
+
BENCH_GIF_FILTER=GIGACHAD npm run bench:decode
|
|
87
|
+
BENCH_GIF_FILTER=tenor npm run bench:decode
|
|
164
88
|
```
|
|
165
89
|
|
|
90
|
+
See [BENCHMARKS.md](BENCHMARKS.md) for the benchmark contract and caveats.
|
|
91
|
+
|
|
166
92
|
[MIT](LICENSE)
|
package/dist/decoder/reader.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class GifReader {
|
|
|
12
12
|
private stack;
|
|
13
13
|
private firstByte;
|
|
14
14
|
private activePreparedFrames;
|
|
15
|
+
private wasmCore;
|
|
15
16
|
private static decodeBackend;
|
|
16
17
|
static createPooled(buf: GifBinary): GifReader;
|
|
17
18
|
static createUnpooled(buf: GifBinary): GifReader;
|
package/dist/encoder/writer.d.ts
CHANGED
|
@@ -43,7 +43,10 @@ export declare class GifWriter {
|
|
|
43
43
|
private loopCount;
|
|
44
44
|
private globalPalette;
|
|
45
45
|
private background;
|
|
46
|
+
private compression;
|
|
46
47
|
private globalColorCount;
|
|
48
|
+
private globalColorTableSizeBits;
|
|
49
|
+
private globalMinCodeSize;
|
|
47
50
|
private previousIndexedFrame;
|
|
48
51
|
constructor(buf: GifBinary, width: number, height: number, gopts?: GifOptions);
|
|
49
52
|
addFrame(x: number, y: number, w: number, h: number, indexedPixels: IndexedPixels, opts?: FrameOptions): number;
|