imagescript 1.3.1 → 3.1.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/LICENSE +4 -1
- package/README.md +76 -26
- package/THIRD_PARTY_NOTICES.md +156 -0
- package/dist/animation.d.ts +55 -0
- package/dist/animation.js +167 -0
- package/dist/codecs/crc.d.ts +9 -0
- package/dist/codecs/crc.js +29 -0
- package/dist/codecs/exif.d.ts +11 -0
- package/dist/codecs/exif.js +27 -0
- package/dist/codecs/gif.d.ts +44 -0
- package/dist/codecs/gif.js +401 -0
- package/dist/codecs/jpeg.d.ts +35 -0
- package/dist/codecs/jpeg.js +980 -0
- package/dist/codecs/png.d.ts +97 -0
- package/dist/codecs/png.js +664 -0
- package/dist/codecs/quantize.d.ts +25 -0
- package/dist/codecs/quantize.js +302 -0
- package/dist/codecs/tga.d.ts +16 -0
- package/dist/codecs/tga.js +203 -0
- package/dist/codecs/tiff.d.ts +6 -0
- package/dist/codecs/tiff.js +546 -0
- package/dist/codecs/vp8-tables.d.ts +10 -0
- package/dist/codecs/vp8-tables.js +17 -0
- package/dist/codecs/vp8.d.ts +8 -0
- package/dist/codecs/vp8.js +711 -0
- package/dist/codecs/vp8l.d.ts +62 -0
- package/dist/codecs/vp8l.js +909 -0
- package/dist/codecs/webp.d.ts +67 -0
- package/dist/codecs/webp.js +280 -0
- package/dist/codecs/zlib.d.ts +22 -0
- package/dist/codecs/zlib.js +607 -0
- package/dist/frame.d.ts +57 -0
- package/dist/frame.js +65 -0
- package/dist/gif.d.ts +31 -0
- package/dist/gif.js +20 -0
- package/dist/image-type.d.ts +24 -0
- package/dist/image-type.js +64 -0
- package/dist/image.d.ts +553 -0
- package/dist/image.js +1156 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +20 -0
- package/dist/package.json +1 -0
- package/dist/render/font.d.ts +92 -0
- package/dist/render/font.js +954 -0
- package/dist/render/hinting.d.ts +110 -0
- package/dist/render/hinting.js +1236 -0
- package/dist/render/svg.d.ts +11 -0
- package/dist/render/svg.js +1717 -0
- package/dist/render/text.d.ts +63 -0
- package/dist/render/text.js +342 -0
- package/dist/text-layout.d.ts +46 -0
- package/dist/text-layout.js +35 -0
- package/dist/util/blur.d.ts +13 -0
- package/dist/util/blur.js +146 -0
- package/dist/util/mem.d.ts +10 -0
- package/dist/util/mem.js +23 -0
- package/dist/util/resample.d.ts +12 -0
- package/dist/util/resample.js +99 -0
- package/dist/util/utf8.d.ts +5 -0
- package/dist/util/utf8.js +55 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/licenses/Apache-2.0.txt +202 -0
- package/licenses/FTL.txt +169 -0
- package/licenses/README.ijg +260 -0
- package/package.json +31 -7
- package/ImageScript.d.ts +0 -954
- package/ImageScript.js +0 -1669
- package/bench/_.mjs +0 -166
- package/bench/all.mjs +0 -4
- package/bench/blur.mjs +0 -59
- package/bench/flip.mjs +0 -63
- package/bench/overlay.mjs +0 -35
- package/bench/package.json +0 -7
- package/bench/readme.md +0 -64
- package/bench/resize.mjs +0 -52
- package/codecs/LICENSE +0 -21
- package/codecs/lib.d.ts +0 -50
- package/codecs/lib.js +0 -6
- package/codecs/lib.mjs +0 -1
- package/codecs/node/bin/arm64-darwin.node +0 -0
- package/codecs/node/bin/arm64-linux.node +0 -0
- package/codecs/node/bin/x64-darwin.node +0 -0
- package/codecs/node/bin/x64-linux.node +0 -0
- package/codecs/node/bin/x64-win32.node +0 -0
- package/codecs/node/index.js +0 -3
- package/codecs/node/index.mjs +0 -6
- package/codecs/package.json +0 -14
- package/codecs/readme.md +0 -38
- package/codecs/wasm/bin/codecs.wasm +0 -0
- package/codecs/wasm/bin/simd.wasm +0 -0
- package/codecs/wasm/bundle/browser.js +0 -1
- package/codecs/wasm/index.js +0 -1
- package/codecs/wasm/index.mjs +0 -1
- package/png/README.md +0 -1
- package/png/node.js +0 -1137
- package/png/src/crc.mjs +0 -49
- package/png/src/mem.mjs +0 -24
- package/png/src/png.mjs +0 -254
- package/png/src/zlib.mjs +0 -618
- package/tsconfig.json +0 -21
- package/utils/mem.js +0 -26
- package/v2/codecs/magic.js +0 -25
- package/v2/framebuffer.d.ts +0 -67
- package/v2/framebuffer.js +0 -2352
- package/v2/framebuffer.mjs +0 -126
- package/v2/framebuffer.rs +0 -1264
- package/v2/index.d.ts +0 -84
- package/v2/index.js +0 -261
- package/v2/ops/blur.mjs +0 -303
- package/v2/ops/color.mjs +0 -277
- package/v2/ops/crop.mjs +0 -64
- package/v2/ops/fill.mjs +0 -33
- package/v2/ops/flip.mjs +0 -33
- package/v2/ops/index.mjs +0 -9
- package/v2/ops/iterator.mjs +0 -34
- package/v2/ops/overlay.mjs +0 -63
- package/v2/ops/resize.mjs +0 -166
- package/v2/ops/rotate.mjs +0 -117
- package/v2/util/mem.mjs +0 -24
- package/wasm/any/font.wasm +0 -0
- package/wasm/any/gif.wasm +0 -0
- package/wasm/any/jpeg.wasm +0 -0
- package/wasm/any/png.wasm +0 -0
- package/wasm/any/svg.wasm +0 -0
- package/wasm/any/tiff.wasm +0 -0
- package/wasm/node/font.js +0 -142
- package/wasm/node/gif.js +0 -76
- package/wasm/node/jpeg.js +0 -92
- package/wasm/node/png.js +0 -49
- package/wasm/node/svg.js +0 -56
- package/wasm/node/tiff.js +0 -47
package/LICENSE
CHANGED
|
@@ -9,4 +9,7 @@ mentioned licenses, but a license must be chosen.
|
|
|
9
9
|
|
|
10
10
|
The corresponding license files can be found in the projects
|
|
11
11
|
root directory, prefixed with LICENSE, suffixed with their
|
|
12
|
-
corresponding SPDX identifier.
|
|
12
|
+
corresponding SPDX identifier.
|
|
13
|
+
|
|
14
|
+
Parts of this software are derived from third party projects under their
|
|
15
|
+
own licenses (MIT, Apache-2.0 and the IJG license), see THIRD_PARTY_NOTICES.md.
|
package/README.md
CHANGED
|
@@ -3,49 +3,99 @@
|
|
|
3
3
|
[](https://discord.gg/8hPrwAH)
|
|
4
4
|
[](https://imagescript.matmen.dev/)
|
|
5
5
|
[](https://github.com/matmen/ImageScript)
|
|
6
|
-
[](https://deno.land/x/imagescript)
|
|
7
6
|
[](https://www.npmjs.com/package/imagescript)
|
|
8
7
|
|
|
9
8
|
---
|
|
10
9
|
|
|
11
|
-
**ImageScript** is a zero-dependency alternative to common JavaScript bitmap image manipulation tools.
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
**ImageScript** is a zero-dependency alternative to common JavaScript bitmap image manipulation tools. Every codec and
|
|
11
|
+
renderer is written in TypeScript: no WebAssembly, no native binaries, no npm dependencies, no install scripts. The
|
|
12
|
+
package is about 140 kB (compressed) and runs in Node.js 22.13 or newer (including Alpine/musl, ARM and bundled
|
|
13
|
+
serverless functions), Deno, Bun and browsers. It works with both `import` and `require()`, type definitions are included.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install imagescript # Node.js, Bun and bundlers
|
|
17
|
+
deno add jsr:@matmen/imagescript # Deno (npm:imagescript works too)
|
|
18
|
+
```
|
|
14
19
|
|
|
15
20
|
---
|
|
16
21
|
|
|
17
22
|
### Features
|
|
18
23
|
|
|
19
|
-
- [Decoding images](https://imagescript.matmen.dev/Image.html
|
|
20
|
-
- PNGs (grayscale, RGB, indexed colors) with and without alpha channels
|
|
21
|
-
- JPEGs (grayscale, RGB, CMYK)
|
|
22
|
-
- TIFFs
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
, [
|
|
35
|
-
, [
|
|
36
|
-
|
|
24
|
+
- [Decoding images](https://imagescript.matmen.dev/classes/Image.html#decode)
|
|
25
|
+
- PNGs (grayscale, RGB, indexed colors, 1-16 bit, interlaced) with and without alpha channels
|
|
26
|
+
- JPEGs (baseline and progressive; grayscale, RGB, CMYK)
|
|
27
|
+
- TIFFs (strips and tiles; uncompressed, LZW, deflate, PackBits and JPEG compressed; BigTIFF)
|
|
28
|
+
- WebPs (lossy, lossless and with alpha; bit-exact with libwebp)
|
|
29
|
+
- TGAs (color mapped, truecolor and greyscale; uncompressed and run length encoded)
|
|
30
|
+
- JPEGs, TIFFs, PNGs and WebPs are rotated as their Exif (or, for PNGs, XMP) orientation says
|
|
31
|
+
- [Decoding animations](https://imagescript.matmen.dev/classes/Animation.html#decode) (GIF, APNG and WebP) with all
|
|
32
|
+
frames, durations and disposal modes
|
|
33
|
+
- [Rendering SVGs](https://imagescript.matmen.dev/classes/Image.html#rendersvg) (shapes, paths, gradients, strokes, clip paths,
|
|
34
|
+
masks, CSS; no text, images or filters)
|
|
35
|
+
- [Rendering vector fonts](https://imagescript.matmen.dev/classes/Image.html#rendertext) (TrueType and OpenType, with kerning,
|
|
36
|
+
outlines and TrueType hinting)
|
|
37
|
+
- [Resizing](https://imagescript.matmen.dev/classes/Image.html#resize) (bilinear, bicubic, area averaging and nearest neighbor)
|
|
38
|
+
- Image manipulation functions ([crop](https://imagescript.matmen.dev/classes/Image.html#crop)
|
|
39
|
+
, [rotate](https://imagescript.matmen.dev/classes/Image.html#rotate)
|
|
40
|
+
, [cropCircle](https://imagescript.matmen.dev/classes/Image.html#cropcircle)
|
|
41
|
+
, [roundCorners](https://imagescript.matmen.dev/classes/Image.html#roundcorners), ...)
|
|
42
|
+
- Filters and color adjustments ([blur](https://imagescript.matmen.dev/classes/Image.html#blur)
|
|
43
|
+
, [sharpen](https://imagescript.matmen.dev/classes/Image.html#sharpen)
|
|
44
|
+
, [brightness](https://imagescript.matmen.dev/classes/Image.html#brightness)
|
|
45
|
+
, [hueShift](https://imagescript.matmen.dev/classes/Image.html#hueshift), ...)
|
|
46
|
+
- Compositing with opacity and blend modes ([composite](https://imagescript.matmen.dev/classes/Image.html#composite)),
|
|
47
|
+
removing backgrounds ([colorToAlpha](https://imagescript.matmen.dev/classes/Image.html#colortoalpha))
|
|
48
|
+
- Color information functions ([averageColor](https://imagescript.matmen.dev/classes/Image.html#averagecolor)
|
|
49
|
+
, [dominantColor](https://imagescript.matmen.dev/classes/Image.html#dominantcolor), ...)
|
|
50
|
+
- Encoding images as [PNGs](https://imagescript.matmen.dev/classes/Image.html#encode) (smallest lossless color type)
|
|
51
|
+
, [JPEGs](https://imagescript.matmen.dev/classes/Image.html#encodejpeg)
|
|
52
|
+
, lossless [WebPs](https://imagescript.matmen.dev/classes/Image.html#encodewebp)
|
|
53
|
+
and [TGAs](https://imagescript.matmen.dev/classes/Image.html#encodetga)
|
|
54
|
+
- Encoding animations as [GIF](https://imagescript.matmen.dev/classes/Animation.html#encodegif),
|
|
55
|
+
[APNG](https://imagescript.matmen.dev/classes/Animation.html#encodeapng) and lossless
|
|
56
|
+
[WebP](https://imagescript.matmen.dev/classes/Animation.html#encodewebp)
|
|
37
57
|
|
|
38
58
|
---
|
|
39
59
|
|
|
40
60
|
### Example
|
|
41
61
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
```js
|
|
63
|
+
import { Image } from 'imagescript';
|
|
64
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
65
|
+
|
|
66
|
+
const image = await Image.decode(await readFile('input.jpg'));
|
|
67
|
+
image.resize(256, Image.RESIZE_AUTO).roundCorners(16);
|
|
68
|
+
await writeFile('output.png', await image.encode());
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
With CommonJS:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
const { Image } = require('imagescript');
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
All coordinates are 0-based, `(0, 0)` is the top left pixel.
|
|
78
|
+
|
|
79
|
+
A bigger example is the [README image generation](https://github.com/matmen/ImageScript/blob/master/tests/readme.js).
|
|
80
|
+
Upgrading from 1.x? Version 3 has breaking changes (0-based coordinates, removed WebP encoding, Node.js 22.13+, ...),
|
|
81
|
+
see the [changelog](https://github.com/matmen/ImageScript/blob/master/CHANGELOG.md). There is no 2.x release, the number was skipped because of the old experimental
|
|
82
|
+
`imagescript/v2` API.
|
|
46
83
|
|
|
47
84
|
[](https://github.com/matmen/ImageScript/blob/master/tests/readme.js)
|
|
48
85
|
|
|
49
86
|
---
|
|
50
87
|
|
|
88
|
+
### Credits
|
|
89
|
+
|
|
90
|
+
The deflate/inflate implementation is derived from [fflate](https://github.com/101arrowz/fflate) and the CRC-32 from
|
|
91
|
+
[Fast CRC32 in JavaScript](https://gist.github.com/101arrowz/e58695f7ccfdf74f60ba22018093edea), both by Arjun Barrett.
|
|
92
|
+
The JPEG codec is based in part on the work of the Independent JPEG Group (libjpeg) and on
|
|
93
|
+
[pdf.js](https://github.com/mozilla/pdf.js), the glyph rasterizer is translated from
|
|
94
|
+
[font-rs](https://github.com/raphlinus/font-rs). The WebP codecs follow [RFC 9649](https://www.rfc-editor.org/rfc/rfc9649) and [RFC 6386](https://www.rfc-editor.org/rfc/rfc6386)
|
|
95
|
+
and reproduce [libwebp](https://chromium.googlesource.com/webm/libwebp)'s color conversion, the TrueType hinting follows
|
|
96
|
+
[FreeType](https://freetype.org)'s interpreter (portions of this software are copyright © 2026 The FreeType Project).
|
|
97
|
+
See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) for their licenses.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
51
101
|
If you have any additional questions, feel free to join the [discord support server](https://discord.gg/8hPrwAH).
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Third party notices
|
|
2
|
+
|
|
3
|
+
ImageScript is licensed under AGPL-3.0-or-later OR MIT (see [LICENSE](LICENSE)). Parts of it are derived from the
|
|
4
|
+
following projects, under their own licenses. Each derived file says so in its header.
|
|
5
|
+
|
|
6
|
+
| Project | Used in | License |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| [fflate](https://github.com/101arrowz/fflate) by Arjun Barrett | `src/codecs/zlib.ts` (deflate/inflate) | MIT |
|
|
9
|
+
| [Fast CRC32 in JavaScript](https://gist.github.com/101arrowz/e58695f7ccfdf74f60ba22018093edea) by Arjun Barrett | `src/codecs/crc.ts` (slice-by-16 CRC-32) | MIT |
|
|
10
|
+
| [libjpeg](https://ijg.org/) / [libjpeg-turbo](https://libjpeg-turbo.org/) by the Independent JPEG Group | `src/codecs/jpeg.ts` (IDCT, FDCT, upsampling, color conversion, progressive refinement, optimal Huffman tables) | IJG |
|
|
11
|
+
| [pdf.js](https://github.com/mozilla/pdf.js) (forked from [jpgjs](https://github.com/notmasteryet/jpgjs) by notmasteryet) | `src/codecs/jpeg.ts` (structure of the scan decoder) | Apache-2.0 |
|
|
12
|
+
| [font-rs](https://github.com/raphlinus/font-rs) by Raph Levien | `src/render/text.ts` (the `Rasterizer` class, also used for SVGs) | Apache-2.0 |
|
|
13
|
+
| [RFC 9649](https://www.rfc-editor.org/rfc/rfc9649) (WebP Image Format) | `src/codecs/vp8l.ts` (lossless bitstream, short distance code table), `src/codecs/webp.ts` (container) | BSD-3-Clause (Revised BSD) |
|
|
14
|
+
| [RFC 6386](https://www.rfc-editor.org/rfc/rfc6386) (VP8 Data Format and Decoding Guide, with the libvpx reference decoder) | `src/codecs/vp8-tables.ts` (probability and quantizer tables), `src/codecs/vp8.ts` (decoding procedures) | BSD-3-Clause |
|
|
15
|
+
| [libwebp](https://chromium.googlesource.com/webm/libwebp) | `src/codecs/vp8.ts` (YUV to RGB conversion, "fancy" chroma upsampling), `src/codecs/webp.ts` (animation frame blending) | BSD-3-Clause |
|
|
16
|
+
| [FreeType](https://freetype.org) | `src/render/hinting.ts` (behavior and parts of the logic of the TrueType interpreter) | FreeType License |
|
|
17
|
+
|
|
18
|
+
## fflate, Fast CRC32 in JavaScript
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
MIT License
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2020 Arjun Barrett (fflate)
|
|
24
|
+
Copyright (c) 2021 Arjun Barrett (Fast CRC32 in JavaScript)
|
|
25
|
+
|
|
26
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
27
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
28
|
+
in the Software without restriction, including without limitation the rights
|
|
29
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
30
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
31
|
+
furnished to do so, subject to the following conditions:
|
|
32
|
+
|
|
33
|
+
The above copyright notice and this permission notice shall be included in all
|
|
34
|
+
copies or substantial portions of the Software.
|
|
35
|
+
|
|
36
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
37
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
38
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
39
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
40
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
41
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
42
|
+
SOFTWARE.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## libjpeg / libjpeg-turbo
|
|
46
|
+
|
|
47
|
+
This software is based in part on the work of the Independent JPEG Group.
|
|
48
|
+
|
|
49
|
+
The JPEG codec translates parts of libjpeg (as shipped by libjpeg-turbo) to TypeScript: the integer inverse DCT
|
|
50
|
+
(`jidctint.c`), the floating point forward DCT (`jfdctflt.c`), fancy upsampling (`jdsample.c`), the YCbCr to RGB
|
|
51
|
+
tables (`jdcolor.c`), the progressive AC refinement (`jdphuff.c`) and the optimal Huffman table generation
|
|
52
|
+
(`jcphuff.c`/`jchuff.c`). The translations were rewritten for TypeScript's numbers and typed arrays; the rest of the
|
|
53
|
+
codec is ImageScript's own. libjpeg's README with its copyright and terms is included unaltered as
|
|
54
|
+
[licenses/README.ijg](licenses/README.ijg).
|
|
55
|
+
|
|
56
|
+
## pdf.js, font-rs
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
pdf.js: Copyright 2014 Mozilla Foundation
|
|
60
|
+
font-rs: Copyright 2015 Google Inc. All rights reserved.
|
|
61
|
+
|
|
62
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
63
|
+
you may not use this file except in compliance with the License.
|
|
64
|
+
You may obtain a copy of the License at
|
|
65
|
+
|
|
66
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
67
|
+
|
|
68
|
+
Unless required by applicable law or agreed to in writing, software
|
|
69
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
70
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
71
|
+
See the License for the specific language governing permissions and
|
|
72
|
+
limitations under the License.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The scan decoder of `src/codecs/jpeg.ts` follows the structure of pdf.js' `src/core/jpg.js` (block and MCU layout,
|
|
76
|
+
one decode function per scan type). The `Rasterizer` class in `src/render/text.ts` is a translation of font-rs'
|
|
77
|
+
`Raster` from Rust to TypeScript (`draw_line`, `draw_quad`, `accumulate`); its `cubic` method is adapted from
|
|
78
|
+
`draw_quad`. The full license text is in [licenses/Apache-2.0.txt](licenses/Apache-2.0.txt).
|
|
79
|
+
|
|
80
|
+
## RFC 6386, libwebp
|
|
81
|
+
|
|
82
|
+
The VP8 decoder follows the procedures of RFC 6386 and its reference decoder, its constant tables are extracted from
|
|
83
|
+
the RFC. To decode exactly like libwebp, the YUV to RGB conversion, the chroma upsampling and the blending of animation
|
|
84
|
+
frames reproduce libwebp's integer arithmetic.
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
RFC 6386 reference decoder: Copyright (c) 2010, 2011, Google Inc. All rights reserved.
|
|
88
|
+
libwebp: Copyright (c) 2010, Google Inc. All rights reserved.
|
|
89
|
+
|
|
90
|
+
Redistribution and use in source and binary forms, with or without
|
|
91
|
+
modification, are permitted provided that the following conditions are
|
|
92
|
+
met:
|
|
93
|
+
|
|
94
|
+
* Redistributions of source code must retain the above copyright
|
|
95
|
+
notice, this list of conditions and the following disclaimer.
|
|
96
|
+
|
|
97
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
98
|
+
notice, this list of conditions and the following disclaimer in
|
|
99
|
+
the documentation and/or other materials provided with the
|
|
100
|
+
distribution.
|
|
101
|
+
|
|
102
|
+
* Neither the name of Google nor the names of its contributors may
|
|
103
|
+
be used to endorse or promote products derived from this software
|
|
104
|
+
without specific prior written permission.
|
|
105
|
+
|
|
106
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
107
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
108
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
109
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
110
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
111
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
112
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
113
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
114
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
115
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
116
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The RFC's reference decoder additionally comes with Google's patent grant for VP8 (RFC 6386 section 20.27).
|
|
120
|
+
|
|
121
|
+
## RFC 9649
|
|
122
|
+
|
|
123
|
+
The WebP container and the lossless (VP8L) codec follow RFC 9649, the short distance code table is taken from it.
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Copyright (c) 2024 IETF Trust and the persons identified as authors of the code. All rights reserved.
|
|
127
|
+
|
|
128
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
|
129
|
+
following conditions are met:
|
|
130
|
+
|
|
131
|
+
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following
|
|
132
|
+
disclaimer.
|
|
133
|
+
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
|
|
134
|
+
following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
135
|
+
- Neither the name of Internet Society, IETF or IETF Trust, nor the names of specific contributors, may be used to
|
|
136
|
+
endorse or promote products derived from this software without specific prior written permission.
|
|
137
|
+
|
|
138
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
139
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
140
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
141
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
142
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
143
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
144
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## FreeType
|
|
148
|
+
|
|
149
|
+
Portions of this software are copyright © 2026 The FreeType Project (https://freetype.org). All rights reserved.
|
|
150
|
+
|
|
151
|
+
The TrueType hinting interpreter in `src/render/hinting.ts` is written after the TrueType instruction specification.
|
|
152
|
+
Where the specification leaves details open, its behavior and parts of its logic follow FreeType's interpreter
|
|
153
|
+
(`src/truetype/ttinterp.c`): the fixed point rounding, measuring original distances on the unscaled outline, the
|
|
154
|
+
interpolation of untouched points (IUP), the handling of near-perpendicular freedom and projection vectors, and the
|
|
155
|
+
tolerance of invalid references. FreeType is used under the FreeType License, included as
|
|
156
|
+
[licenses/FTL.txt](licenses/FTL.txt).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { BinaryData } from './util/mem.js';
|
|
2
|
+
import { ResizeMode } from './image.js';
|
|
3
|
+
import { Frame } from './frame.js';
|
|
4
|
+
/**
|
|
5
|
+
* An animation: an array of frames that can be decoded from and encoded to GIF, APNG and WebP.
|
|
6
|
+
* Array methods like map and filter return plain arrays.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Animation extends Array<Frame> {
|
|
9
|
+
/** How many times to repeat the animation (-1 = forever, 0 = play once) */
|
|
10
|
+
loopCount: number;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new animation
|
|
13
|
+
* @param frames The frames to create the animation from
|
|
14
|
+
* @param loopCount How many times to repeat the animation (-1 = forever, 0 = play once)
|
|
15
|
+
*/
|
|
16
|
+
constructor(frames: Frame[], loopCount?: number);
|
|
17
|
+
/** The animation's width */
|
|
18
|
+
get width(): number;
|
|
19
|
+
/** The animation's height */
|
|
20
|
+
get height(): number;
|
|
21
|
+
/** The animation's duration (in ms) */
|
|
22
|
+
get duration(): number;
|
|
23
|
+
toString(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Returns a new animation of the same class containing a range of this one's frames
|
|
26
|
+
* (same semantics as Array.prototype.slice)
|
|
27
|
+
*/
|
|
28
|
+
slice(start?: number, end?: number): this;
|
|
29
|
+
/**
|
|
30
|
+
* Resizes the animation to the given dimensions, scaling every frame and its offsets proportionally.
|
|
31
|
+
* Use {@link Image.RESIZE_AUTO} as either width or height to automatically preserve the aspect ratio.
|
|
32
|
+
*/
|
|
33
|
+
resize(width: number, height: number, mode?: ResizeMode): this;
|
|
34
|
+
/**
|
|
35
|
+
* Encodes the animation as GIF
|
|
36
|
+
* @param quality 1-100, scales the palette size from 8 colors (1) to 256 colors (90 and above)
|
|
37
|
+
*/
|
|
38
|
+
encodeGIF(quality?: number): Promise<Uint8Array>;
|
|
39
|
+
/**
|
|
40
|
+
* Encodes the animation as APNG (animated PNG, lossless and with full alpha). Viewers without APNG support show
|
|
41
|
+
* the first frame
|
|
42
|
+
* @param compression The zlib compression level (0-9), higher is smaller but slower
|
|
43
|
+
*/
|
|
44
|
+
encodeAPNG(compression?: number): Promise<Uint8Array>;
|
|
45
|
+
/**
|
|
46
|
+
* Encodes the animation as lossless animated WebP
|
|
47
|
+
*/
|
|
48
|
+
encodeWebP(): Promise<Uint8Array>;
|
|
49
|
+
/**
|
|
50
|
+
* Decodes an animated GIF, APNG or WebP into fully composited frames (still images become a single frame)
|
|
51
|
+
* @param input The binary data to decode
|
|
52
|
+
* @param onlyExtractFirstFrame Whether to stop decoding after the first frame
|
|
53
|
+
*/
|
|
54
|
+
static decode(input: BinaryData, onlyExtractFirstFrame?: boolean): Promise<Animation>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as gifCodec from "./codecs/gif.js";
|
|
2
|
+
import * as png from "./codecs/png.js";
|
|
3
|
+
import * as webp from "./codecs/webp.js";
|
|
4
|
+
import { view } from "./util/mem.js";
|
|
5
|
+
import { Image } from "./image.js";
|
|
6
|
+
import { Frame } from "./frame.js";
|
|
7
|
+
import { ImageType } from "./image-type.js";
|
|
8
|
+
const toPlays = (loopCount) => loopCount < 0 ? 0 : loopCount + 1;
|
|
9
|
+
const fromPlays = (plays) => plays === 0 ? -1 : plays - 1;
|
|
10
|
+
function canvasFrames(width, height, frames) {
|
|
11
|
+
return frames.map(({ pixels, duration }) => {
|
|
12
|
+
const frame = new Frame(width, height, duration, 0, 0, Frame.DISPOSAL_BACKGROUND);
|
|
13
|
+
frame.bitmap.set(pixels);
|
|
14
|
+
return frame;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export class Animation extends Array {
|
|
18
|
+
loopCount;
|
|
19
|
+
constructor(frames, loopCount = -1) {
|
|
20
|
+
super();
|
|
21
|
+
for (const frame of frames)
|
|
22
|
+
this.push(frame);
|
|
23
|
+
for (let i = 0; i < this.length; i++)
|
|
24
|
+
if (!(this[i] instanceof Frame))
|
|
25
|
+
throw new TypeError(`Frame ${i} is not an instance of Frame`);
|
|
26
|
+
if (loopCount < -1 || isNaN(loopCount))
|
|
27
|
+
throw new RangeError('Invalid loop count');
|
|
28
|
+
this.loopCount = loopCount;
|
|
29
|
+
}
|
|
30
|
+
static get [Symbol.species]() {
|
|
31
|
+
return Array;
|
|
32
|
+
}
|
|
33
|
+
get width() {
|
|
34
|
+
let max = 0;
|
|
35
|
+
for (const frame of this)
|
|
36
|
+
max = Math.max(max, frame.width + frame.xOffset);
|
|
37
|
+
return max;
|
|
38
|
+
}
|
|
39
|
+
get height() {
|
|
40
|
+
let max = 0;
|
|
41
|
+
for (const frame of this)
|
|
42
|
+
max = Math.max(max, frame.height + frame.yOffset);
|
|
43
|
+
return max;
|
|
44
|
+
}
|
|
45
|
+
get duration() {
|
|
46
|
+
let duration = 0;
|
|
47
|
+
for (const frame of this)
|
|
48
|
+
duration += frame.duration;
|
|
49
|
+
return duration;
|
|
50
|
+
}
|
|
51
|
+
toString() {
|
|
52
|
+
return `Animation<${this.width}x${this.height}x${this.duration}ms>`;
|
|
53
|
+
}
|
|
54
|
+
slice(start, end) {
|
|
55
|
+
const Class = this.constructor;
|
|
56
|
+
return new Class(Array.prototype.slice.call(this, start, end), this.loopCount);
|
|
57
|
+
}
|
|
58
|
+
resize(width, height, mode = Image.RESIZE_BILINEAR) {
|
|
59
|
+
const originalWidth = this.width, originalHeight = this.height;
|
|
60
|
+
if (width === Image.RESIZE_AUTO && height === Image.RESIZE_AUTO)
|
|
61
|
+
throw new Error('RESIZE_AUTO can only be used for either width or height, not for both');
|
|
62
|
+
if (width === Image.RESIZE_AUTO)
|
|
63
|
+
width = originalWidth / originalHeight * height;
|
|
64
|
+
if (height === Image.RESIZE_AUTO)
|
|
65
|
+
height = originalHeight / originalWidth * width;
|
|
66
|
+
const sx = width / originalWidth, sy = height / originalHeight;
|
|
67
|
+
for (const frame of this) {
|
|
68
|
+
const left = Math.round(frame.xOffset * sx), top = Math.round(frame.yOffset * sy);
|
|
69
|
+
const right = Math.round((frame.xOffset + frame.width) * sx), bottom = Math.round((frame.yOffset + frame.height) * sy);
|
|
70
|
+
Image.prototype.resize.call(frame, Math.max(1, right - left), Math.max(1, bottom - top), mode);
|
|
71
|
+
frame.xOffset = left;
|
|
72
|
+
frame.yOffset = top;
|
|
73
|
+
}
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
__validate__() {
|
|
77
|
+
for (const frame of this)
|
|
78
|
+
if (!(frame instanceof Frame))
|
|
79
|
+
throw new Error('Animation contains invalid frames');
|
|
80
|
+
}
|
|
81
|
+
async encodeGIF(quality = 95) {
|
|
82
|
+
this.__validate__();
|
|
83
|
+
const frames = [];
|
|
84
|
+
for (const frame of this)
|
|
85
|
+
frames.push({
|
|
86
|
+
x: frame.xOffset,
|
|
87
|
+
y: frame.yOffset,
|
|
88
|
+
width: frame.width,
|
|
89
|
+
height: frame.height,
|
|
90
|
+
delay: Math.round(frame.duration / 10),
|
|
91
|
+
dispose: frame.__disposal_id__,
|
|
92
|
+
pixels: new Uint8Array(frame.bitmap.buffer, frame.bitmap.byteOffset, frame.bitmap.byteLength)
|
|
93
|
+
});
|
|
94
|
+
return gifCodec.encode({ width: this.width, height: this.height, loopCount: this.loopCount, frames }, quality);
|
|
95
|
+
}
|
|
96
|
+
async encodeAPNG(compression = 1) {
|
|
97
|
+
this.__validate__();
|
|
98
|
+
if (!this.length)
|
|
99
|
+
throw new Error('An animation needs at least one frame');
|
|
100
|
+
return png.encodeAnimation(this.__apngFrames__(), this.width, this.height, toPlays(this.loopCount), compression);
|
|
101
|
+
}
|
|
102
|
+
async encodeWebP() {
|
|
103
|
+
this.__validate__();
|
|
104
|
+
if (!this.length)
|
|
105
|
+
throw new Error('An animation needs at least one frame');
|
|
106
|
+
const width = this.width, height = this.height;
|
|
107
|
+
const fullFrames = this.some(f => f.disposalMode === 'previous' || (f.disposalMode === 'background' && (f.xOffset & 1 || f.yOffset & 1)));
|
|
108
|
+
if (fullFrames) {
|
|
109
|
+
const canvases = png.compositeAnimation({ width, height, frames: this.__apngFrames__() });
|
|
110
|
+
return webp.encodeAnimation(canvases.map(c => ({ x: 0, y: 0, width, height, duration: c.duration, dispose: true, pixels: c.pixels })), width, height, toPlays(this.loopCount));
|
|
111
|
+
}
|
|
112
|
+
const frames = Array.from(this, frame => {
|
|
113
|
+
let { xOffset: x, yOffset: y, width: w, height: h } = frame;
|
|
114
|
+
let pixels = new Uint8Array(frame.bitmap.buffer, frame.bitmap.byteOffset, frame.bitmap.byteLength);
|
|
115
|
+
if (x & 1 || y & 1) {
|
|
116
|
+
const dx = x & 1, dy = y & 1;
|
|
117
|
+
const padded = new Uint8Array((w + dx) * (h + dy) * 4);
|
|
118
|
+
for (let row = 0; row < h; row++)
|
|
119
|
+
padded.set(pixels.subarray(row * w * 4, (row + 1) * w * 4), ((row + dy) * (w + dx) + dx) * 4);
|
|
120
|
+
pixels = padded;
|
|
121
|
+
x -= dx;
|
|
122
|
+
y -= dy;
|
|
123
|
+
w += dx;
|
|
124
|
+
h += dy;
|
|
125
|
+
}
|
|
126
|
+
return { x, y, width: w, height: h, duration: frame.duration, dispose: frame.disposalMode === 'background', pixels };
|
|
127
|
+
});
|
|
128
|
+
return webp.encodeAnimation(frames, width, height, toPlays(this.loopCount));
|
|
129
|
+
}
|
|
130
|
+
__apngFrames__() {
|
|
131
|
+
const dispose = [0, 0, 2, 1];
|
|
132
|
+
return Array.from(this, frame => ({
|
|
133
|
+
x: frame.xOffset,
|
|
134
|
+
y: frame.yOffset,
|
|
135
|
+
width: frame.width,
|
|
136
|
+
height: frame.height,
|
|
137
|
+
duration: frame.duration,
|
|
138
|
+
dispose: dispose[frame.__disposal_id__],
|
|
139
|
+
blend: 1,
|
|
140
|
+
pixels: new Uint8Array(frame.bitmap.buffer, frame.bitmap.byteOffset, frame.bitmap.byteLength)
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
static async decode(input, onlyExtractFirstFrame = false) {
|
|
144
|
+
const data = view(input);
|
|
145
|
+
const type = ImageType.getType(data);
|
|
146
|
+
if (type === 'gif')
|
|
147
|
+
return Animation.__fromGIF__(data, onlyExtractFirstFrame, Animation);
|
|
148
|
+
if (type === 'webp') {
|
|
149
|
+
const decoded = webp.decodeAnimation(data, onlyExtractFirstFrame);
|
|
150
|
+
const frames = canvasFrames(decoded.width, decoded.height, webp.composite(decoded));
|
|
151
|
+
return new Animation(frames, fromPlays(decoded.loopCount));
|
|
152
|
+
}
|
|
153
|
+
if (type === 'png') {
|
|
154
|
+
const decoded = await png.decodeAnimation(data, onlyExtractFirstFrame);
|
|
155
|
+
const frames = canvasFrames(decoded.width, decoded.height, png.compositeAnimation(decoded));
|
|
156
|
+
return new Animation(frames, decoded.frames.length > 1 ? fromPlays(decoded.plays) : -1);
|
|
157
|
+
}
|
|
158
|
+
if (type)
|
|
159
|
+
return new Animation([Frame.from(await Image.decode(data), 0)]);
|
|
160
|
+
throw new Error('Unsupported image type');
|
|
161
|
+
}
|
|
162
|
+
static __fromGIF__(data, onlyExtractFirstFrame, Class) {
|
|
163
|
+
const decoded = gifCodec.decode(data, onlyExtractFirstFrame);
|
|
164
|
+
const frames = gifCodec.composite(decoded).map(({ pixels, delay }) => ({ pixels, duration: 10 * delay }));
|
|
165
|
+
return new Class(canvasFrames(decoded.width, decoded.height, frames), decoded.loopCount);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Slice-by-16 CRC-32, derived from "Fast CRC32 in JavaScript" (https://gist.github.com/101arrowz/e58695f7ccfdf74f60ba22018093edea)
|
|
3
|
+
* Copyright (c) 2021 Arjun Barrett, MIT License (see THIRD_PARTY_NOTICES.md)
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* CRC-32 (ISO 3309), as used by PNG and zlib. Processes 16 bytes per step (slice-by-16, see #12), byte by byte so
|
|
7
|
+
* it doesn't depend on alignment or endianness
|
|
8
|
+
*/
|
|
9
|
+
export declare function crc32(buffer: Uint8Array): number;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Slice-by-16 CRC-32, derived from "Fast CRC32 in JavaScript" (https://gist.github.com/101arrowz/e58695f7ccfdf74f60ba22018093edea)
|
|
3
|
+
* Copyright (c) 2021 Arjun Barrett, MIT License (see THIRD_PARTY_NOTICES.md)
|
|
4
|
+
*/
|
|
5
|
+
const TABLE = new Int32Array(16 * 256);
|
|
6
|
+
for (let n = 0; n < 256; n++) {
|
|
7
|
+
let c = n;
|
|
8
|
+
for (let k = 0; k < 8; k++)
|
|
9
|
+
c = c & 1 ? 0xedb88320 ^ c >>> 1 : c >>> 1;
|
|
10
|
+
TABLE[n] = c;
|
|
11
|
+
}
|
|
12
|
+
for (let n = 0; n < 256; n++) {
|
|
13
|
+
let c = TABLE[n];
|
|
14
|
+
for (let k = 256; k < TABLE.length; k += 256)
|
|
15
|
+
c = TABLE[k + n] = TABLE[c & 0xff] ^ c >>> 8;
|
|
16
|
+
}
|
|
17
|
+
export function crc32(buffer) {
|
|
18
|
+
let crc = -1, i = 0;
|
|
19
|
+
for (const end = buffer.length - 16; i <= end; i += 16) {
|
|
20
|
+
crc = TABLE[3840 + ((buffer[i] ^ crc) & 0xff)] ^ TABLE[3584 + ((buffer[i + 1] ^ crc >> 8) & 0xff)]
|
|
21
|
+
^ TABLE[3328 + ((buffer[i + 2] ^ crc >> 16) & 0xff)] ^ TABLE[3072 + (buffer[i + 3] ^ crc >>> 24)]
|
|
22
|
+
^ TABLE[2816 + buffer[i + 4]] ^ TABLE[2560 + buffer[i + 5]] ^ TABLE[2304 + buffer[i + 6]] ^ TABLE[2048 + buffer[i + 7]]
|
|
23
|
+
^ TABLE[1792 + buffer[i + 8]] ^ TABLE[1536 + buffer[i + 9]] ^ TABLE[1280 + buffer[i + 10]] ^ TABLE[1024 + buffer[i + 11]]
|
|
24
|
+
^ TABLE[768 + buffer[i + 12]] ^ TABLE[512 + buffer[i + 13]] ^ TABLE[256 + buffer[i + 14]] ^ TABLE[buffer[i + 15]];
|
|
25
|
+
}
|
|
26
|
+
for (; i < buffer.length; i++)
|
|
27
|
+
crc = TABLE[(crc ^ buffer[i]) & 0xff] ^ crc >>> 8;
|
|
28
|
+
return (crc ^ -1) >>> 0;
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the orientation tag (0x0112) from the first IFD of Exif data
|
|
3
|
+
* @param data a TIFF header and IFDs, optionally preceded by "Exif\0\0" (as in JPEG's APP1)
|
|
4
|
+
* @return the orientation (1-8), 1 if it is missing or invalid
|
|
5
|
+
*/
|
|
6
|
+
export declare function orientation(data: Uint8Array): number;
|
|
7
|
+
/**
|
|
8
|
+
* Reads tiff:Orientation from an XMP packet, as attribute or element
|
|
9
|
+
* @return the orientation (1-8), 1 if it is missing or invalid
|
|
10
|
+
*/
|
|
11
|
+
export declare function xmpOrientation(xmp: string): number;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function orientation(data) {
|
|
2
|
+
if (data.length >= 6 && data[0] === 0x45 && data[1] === 0x78 && data[2] === 0x69 && data[3] === 0x66 && data[4] === 0 && data[5] === 0)
|
|
3
|
+
data = data.subarray(6);
|
|
4
|
+
if (data.length < 8)
|
|
5
|
+
return 1;
|
|
6
|
+
const tiff = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
7
|
+
const little = tiff.getUint16(0) === 0x4949;
|
|
8
|
+
if (!little && tiff.getUint16(0) !== 0x4d4d)
|
|
9
|
+
return 1;
|
|
10
|
+
const ifd = tiff.getUint32(4, little);
|
|
11
|
+
if (ifd + 2 > tiff.byteLength)
|
|
12
|
+
return 1;
|
|
13
|
+
for (let i = 0, count = tiff.getUint16(ifd, little); i < count; i++) {
|
|
14
|
+
const entry = ifd + 2 + i * 12;
|
|
15
|
+
if (entry + 12 > tiff.byteLength)
|
|
16
|
+
break;
|
|
17
|
+
if (tiff.getUint16(entry, little) !== 0x0112)
|
|
18
|
+
continue;
|
|
19
|
+
const value = tiff.getUint16(entry + 8, little);
|
|
20
|
+
return value >= 1 && value <= 8 ? value : 1;
|
|
21
|
+
}
|
|
22
|
+
return 1;
|
|
23
|
+
}
|
|
24
|
+
export function xmpOrientation(xmp) {
|
|
25
|
+
const match = /tiff:Orientation\s*(?:=\s*["']|>\s*)([1-8])\b/.exec(xmp);
|
|
26
|
+
return match ? +match[1] : 1;
|
|
27
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface GIFFrame {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
/** Frame delay in centiseconds */
|
|
7
|
+
delay: number;
|
|
8
|
+
/** ImageScript disposal mode (0 any, 1 keep, 2 previous, 3 background) */
|
|
9
|
+
dispose: number;
|
|
10
|
+
/** RGBA pixel data */
|
|
11
|
+
pixels: Uint8Array;
|
|
12
|
+
}
|
|
13
|
+
export interface GIFData {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
/** -1 loops forever, 0 plays once, n repeats n times */
|
|
17
|
+
loopCount: number;
|
|
18
|
+
frames: GIFFrame[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Encodes frames into a GIF
|
|
22
|
+
* @param {{width: number, height: number, loopCount?: number, frames: GIFFrame[]}} gif loopCount -1 loops forever, 0 plays once, n repeats n times
|
|
23
|
+
* @param {number} [quality=95] 1-100, scales the palette size (1 = 8 colors, 50 = 55, 90 and above = 256)
|
|
24
|
+
* @return {Uint8Array}
|
|
25
|
+
*/
|
|
26
|
+
export declare function encode({ width, height, loopCount, frames }: Omit<GIFData, 'loopCount'> & {
|
|
27
|
+
loopCount?: number;
|
|
28
|
+
}, quality?: number): Uint8Array;
|
|
29
|
+
/**
|
|
30
|
+
* Decodes a GIF into its individual (uncomposited) frames, zero-sized images are skipped
|
|
31
|
+
* @param {Uint8Array} data
|
|
32
|
+
* @param {boolean} [onlyFirstFrame=false]
|
|
33
|
+
* @return {{width: number, height: number, loopCount: number, frames: GIFFrame[]}}
|
|
34
|
+
*/
|
|
35
|
+
export declare function decode(data: Uint8Array, onlyFirstFrame?: boolean): GIFData;
|
|
36
|
+
/**
|
|
37
|
+
* Composites decoded frames onto the logical screen, applying their disposal modes
|
|
38
|
+
* @param {{width: number, height: number, frames: GIFFrame[]}} gif output of {@link decode}
|
|
39
|
+
* @return {Array<{pixels: Uint8Array, delay: number}>} full screen RGBA frames
|
|
40
|
+
*/
|
|
41
|
+
export declare function composite({ width, height, frames }: GIFData): Array<{
|
|
42
|
+
pixels: Uint8Array;
|
|
43
|
+
delay: number;
|
|
44
|
+
}>;
|