imagescript 1.3.1 → 3.0.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.
Files changed (117) hide show
  1. package/LICENSE +4 -1
  2. package/README.md +64 -26
  3. package/THIRD_PARTY_NOTICES.md +74 -0
  4. package/dist/codecs/crc.d.ts +9 -0
  5. package/dist/codecs/crc.js +29 -0
  6. package/dist/codecs/exif.d.ts +11 -0
  7. package/dist/codecs/exif.js +27 -0
  8. package/dist/codecs/gif.d.ts +44 -0
  9. package/dist/codecs/gif.js +401 -0
  10. package/dist/codecs/jpeg.d.ts +35 -0
  11. package/dist/codecs/jpeg.js +980 -0
  12. package/dist/codecs/png.d.ts +44 -0
  13. package/dist/codecs/png.js +442 -0
  14. package/dist/codecs/quantize.d.ts +25 -0
  15. package/dist/codecs/quantize.js +302 -0
  16. package/dist/codecs/tiff.d.ts +6 -0
  17. package/dist/codecs/tiff.js +546 -0
  18. package/dist/codecs/zlib.d.ts +22 -0
  19. package/dist/codecs/zlib.js +607 -0
  20. package/dist/frame.d.ts +57 -0
  21. package/dist/frame.js +65 -0
  22. package/dist/gif.d.ts +44 -0
  23. package/dist/gif.js +93 -0
  24. package/dist/image-type.d.ts +20 -0
  25. package/dist/image-type.js +49 -0
  26. package/dist/image.d.ts +521 -0
  27. package/dist/image.js +1118 -0
  28. package/dist/index.d.ts +19 -0
  29. package/dist/index.js +13 -0
  30. package/dist/package.json +1 -0
  31. package/dist/render/font.d.ts +63 -0
  32. package/dist/render/font.js +857 -0
  33. package/dist/render/svg.d.ts +11 -0
  34. package/dist/render/svg.js +1717 -0
  35. package/dist/render/text.d.ts +61 -0
  36. package/dist/render/text.js +317 -0
  37. package/dist/text-layout.d.ts +46 -0
  38. package/dist/text-layout.js +35 -0
  39. package/dist/util/blur.d.ts +13 -0
  40. package/dist/util/blur.js +146 -0
  41. package/dist/util/mem.d.ts +10 -0
  42. package/dist/util/mem.js +23 -0
  43. package/dist/util/resample.d.ts +12 -0
  44. package/dist/util/resample.js +99 -0
  45. package/dist/util/utf8.d.ts +5 -0
  46. package/dist/util/utf8.js +55 -0
  47. package/dist/version.d.ts +1 -0
  48. package/dist/version.js +1 -0
  49. package/licenses/Apache-2.0.txt +202 -0
  50. package/licenses/README.ijg +260 -0
  51. package/package.json +31 -7
  52. package/ImageScript.d.ts +0 -954
  53. package/ImageScript.js +0 -1669
  54. package/bench/_.mjs +0 -166
  55. package/bench/all.mjs +0 -4
  56. package/bench/blur.mjs +0 -59
  57. package/bench/flip.mjs +0 -63
  58. package/bench/overlay.mjs +0 -35
  59. package/bench/package.json +0 -7
  60. package/bench/readme.md +0 -64
  61. package/bench/resize.mjs +0 -52
  62. package/codecs/LICENSE +0 -21
  63. package/codecs/lib.d.ts +0 -50
  64. package/codecs/lib.js +0 -6
  65. package/codecs/lib.mjs +0 -1
  66. package/codecs/node/bin/arm64-darwin.node +0 -0
  67. package/codecs/node/bin/arm64-linux.node +0 -0
  68. package/codecs/node/bin/x64-darwin.node +0 -0
  69. package/codecs/node/bin/x64-linux.node +0 -0
  70. package/codecs/node/bin/x64-win32.node +0 -0
  71. package/codecs/node/index.js +0 -3
  72. package/codecs/node/index.mjs +0 -6
  73. package/codecs/package.json +0 -14
  74. package/codecs/readme.md +0 -38
  75. package/codecs/wasm/bin/codecs.wasm +0 -0
  76. package/codecs/wasm/bin/simd.wasm +0 -0
  77. package/codecs/wasm/bundle/browser.js +0 -1
  78. package/codecs/wasm/index.js +0 -1
  79. package/codecs/wasm/index.mjs +0 -1
  80. package/png/README.md +0 -1
  81. package/png/node.js +0 -1137
  82. package/png/src/crc.mjs +0 -49
  83. package/png/src/mem.mjs +0 -24
  84. package/png/src/png.mjs +0 -254
  85. package/png/src/zlib.mjs +0 -618
  86. package/tsconfig.json +0 -21
  87. package/utils/mem.js +0 -26
  88. package/v2/codecs/magic.js +0 -25
  89. package/v2/framebuffer.d.ts +0 -67
  90. package/v2/framebuffer.js +0 -2352
  91. package/v2/framebuffer.mjs +0 -126
  92. package/v2/framebuffer.rs +0 -1264
  93. package/v2/index.d.ts +0 -84
  94. package/v2/index.js +0 -261
  95. package/v2/ops/blur.mjs +0 -303
  96. package/v2/ops/color.mjs +0 -277
  97. package/v2/ops/crop.mjs +0 -64
  98. package/v2/ops/fill.mjs +0 -33
  99. package/v2/ops/flip.mjs +0 -33
  100. package/v2/ops/index.mjs +0 -9
  101. package/v2/ops/iterator.mjs +0 -34
  102. package/v2/ops/overlay.mjs +0 -63
  103. package/v2/ops/resize.mjs +0 -166
  104. package/v2/ops/rotate.mjs +0 -117
  105. package/v2/util/mem.mjs +0 -24
  106. package/wasm/any/font.wasm +0 -0
  107. package/wasm/any/gif.wasm +0 -0
  108. package/wasm/any/jpeg.wasm +0 -0
  109. package/wasm/any/png.wasm +0 -0
  110. package/wasm/any/svg.wasm +0 -0
  111. package/wasm/any/tiff.wasm +0 -0
  112. package/wasm/node/font.js +0 -142
  113. package/wasm/node/gif.js +0 -76
  114. package/wasm/node/jpeg.js +0 -92
  115. package/wasm/node/png.js +0 -49
  116. package/wasm/node/svg.js +0 -56
  117. package/wasm/node/tiff.js +0 -47
package/dist/frame.js ADDED
@@ -0,0 +1,65 @@
1
+ import { Image } from "./image.js";
2
+ const DISPOSAL_MODES = ['any', 'keep', 'previous', 'background'];
3
+ export class Frame extends Image {
4
+ duration;
5
+ xOffset;
6
+ yOffset;
7
+ __disposalMode__ = 1;
8
+ static get DISPOSAL_ANY() {
9
+ return 'any';
10
+ }
11
+ static get DISPOSAL_KEEP() {
12
+ return 'keep';
13
+ }
14
+ static get DISPOSAL_PREVIOUS() {
15
+ return 'previous';
16
+ }
17
+ static get DISPOSAL_BACKGROUND() {
18
+ return 'background';
19
+ }
20
+ static __convert_disposal_mode__(mode) {
21
+ const id = typeof mode === 'string' ? DISPOSAL_MODES.indexOf(mode) : mode;
22
+ if (!(id >= 0 && id <= 3))
23
+ throw new RangeError('Invalid disposal mode');
24
+ return id;
25
+ }
26
+ constructor(width, height, duration = 100, xOffset = 0, yOffset = 0, disposalMode = Frame.DISPOSAL_KEEP) {
27
+ if (isNaN(duration) || duration < 0)
28
+ throw new RangeError('Invalid frame duration');
29
+ super(width, height);
30
+ this.duration = duration;
31
+ this.xOffset = xOffset;
32
+ this.yOffset = yOffset;
33
+ this.disposalMode = disposalMode;
34
+ }
35
+ get disposalMode() {
36
+ return DISPOSAL_MODES[this.__disposalMode__];
37
+ }
38
+ get __disposal_id__() {
39
+ return this.__disposalMode__;
40
+ }
41
+ set disposalMode(disposalMode) {
42
+ this.__disposalMode__ = Frame.__convert_disposal_mode__(disposalMode);
43
+ }
44
+ toString() {
45
+ return `Frame<${this.width}x${this.height}x${this.duration}ms>`;
46
+ }
47
+ static from(image, duration, xOffset, yOffset, disposalMode = Frame.DISPOSAL_KEEP) {
48
+ if (!(image instanceof Image))
49
+ throw new TypeError('Invalid image passed');
50
+ const frame = new Frame(image.width, image.height, duration, xOffset, yOffset, disposalMode);
51
+ frame.bitmap.set(image.bitmap);
52
+ return frame;
53
+ }
54
+ clone() {
55
+ return Frame.from(this, this.duration, this.xOffset, this.yOffset, this.disposalMode);
56
+ }
57
+ resize(width, height, mode = Image.RESIZE_BILINEAR) {
58
+ const originalWidth = this.width;
59
+ const originalHeight = this.height;
60
+ super.resize(width, height, mode);
61
+ this.xOffset = Math.round(this.xOffset * this.width / originalWidth);
62
+ this.yOffset = Math.round(this.yOffset * this.height / originalHeight);
63
+ return this;
64
+ }
65
+ }
package/dist/gif.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { BinaryData } from './util/mem.js';
2
+ import { ResizeMode } from './image.js';
3
+ import { Frame } from './frame.js';
4
+ /**
5
+ * Represents a GIF image as an array of frames.
6
+ * Array methods like map and filter return plain arrays.
7
+ */
8
+ export declare class GIF extends Array<Frame> {
9
+ /** How many times to repeat the GIF (-1 = forever, 0 = play once) */
10
+ loopCount: number;
11
+ /**
12
+ * Creates a new GIF image
13
+ * @param frames The frames to create the GIF from
14
+ * @param loopCount How many times to repeat the GIF (-1 = forever, 0 = play once)
15
+ */
16
+ constructor(frames: Frame[], loopCount?: number);
17
+ /** The GIF's width */
18
+ get width(): number;
19
+ /** The GIF's height */
20
+ get height(): number;
21
+ /** The GIF's duration (in ms) */
22
+ get duration(): number;
23
+ toString(): string;
24
+ /**
25
+ * Returns a new GIF containing a range of this GIF's frames (same semantics as Array.prototype.slice)
26
+ */
27
+ slice(start?: number, end?: number): GIF;
28
+ /**
29
+ * Encodes the GIF
30
+ * @param quality 1-100, scales the palette size from 8 colors (1) to 256 colors (90 and above)
31
+ */
32
+ encode(quality?: number): Promise<Uint8Array>;
33
+ /**
34
+ * Decodes a GIF into fully composited frames
35
+ * @param input The binary data to decode
36
+ * @param onlyExtractFirstFrame Whether to end GIF decoding after the first frame
37
+ */
38
+ static decode(input: BinaryData, onlyExtractFirstFrame?: boolean): Promise<GIF>;
39
+ /**
40
+ * Resizes the GIF to the given dimensions, scaling every frame and its offsets proportionally.
41
+ * Use {@link Image.RESIZE_AUTO} as either width or height to automatically preserve the aspect ratio.
42
+ */
43
+ resize(width: number, height: number, mode?: ResizeMode): this;
44
+ }
package/dist/gif.js ADDED
@@ -0,0 +1,93 @@
1
+ import * as codec from "./codecs/gif.js";
2
+ import { view } from "./util/mem.js";
3
+ import { Image } from "./image.js";
4
+ import { Frame } from "./frame.js";
5
+ import { ImageType } from "./image-type.js";
6
+ export class GIF extends Array {
7
+ loopCount;
8
+ constructor(frames, loopCount = -1) {
9
+ super();
10
+ for (const frame of frames)
11
+ this.push(frame);
12
+ for (let i = 0; i < this.length; i++)
13
+ if (!(this[i] instanceof Frame))
14
+ throw new TypeError(`Frame ${i} is not an instance of Frame`);
15
+ if (loopCount < -1 || isNaN(loopCount))
16
+ throw new RangeError('Invalid loop count');
17
+ this.loopCount = loopCount;
18
+ }
19
+ static get [Symbol.species]() {
20
+ return Array;
21
+ }
22
+ get width() {
23
+ let max = 0;
24
+ for (const frame of this)
25
+ max = Math.max(max, frame.width + frame.xOffset);
26
+ return max;
27
+ }
28
+ get height() {
29
+ let max = 0;
30
+ for (const frame of this)
31
+ max = Math.max(max, frame.height + frame.yOffset);
32
+ return max;
33
+ }
34
+ get duration() {
35
+ let duration = 0;
36
+ for (const frame of this)
37
+ duration += frame.duration;
38
+ return duration;
39
+ }
40
+ toString() {
41
+ return `GIF<${this.width}x${this.height}x${this.duration}ms>`;
42
+ }
43
+ slice(start, end) {
44
+ return new GIF(Array.prototype.slice.call(this, start, end), this.loopCount);
45
+ }
46
+ async encode(quality = 95) {
47
+ const frames = [];
48
+ for (const frame of this) {
49
+ if (!(frame instanceof Frame))
50
+ throw new Error('GIF contains invalid frames');
51
+ frames.push({
52
+ x: frame.xOffset,
53
+ y: frame.yOffset,
54
+ width: frame.width,
55
+ height: frame.height,
56
+ delay: Math.round(frame.duration / 10),
57
+ dispose: frame.__disposal_id__,
58
+ pixels: new Uint8Array(frame.bitmap.buffer, frame.bitmap.byteOffset, frame.bitmap.byteLength)
59
+ });
60
+ }
61
+ return codec.encode({ width: this.width, height: this.height, loopCount: this.loopCount, frames }, quality);
62
+ }
63
+ static async decode(input, onlyExtractFirstFrame = false) {
64
+ const data = view(input);
65
+ if (!ImageType.isGIF(new DataView(data.buffer, data.byteOffset, data.byteLength)))
66
+ throw new Error('Unsupported image type');
67
+ const decoded = codec.decode(data, onlyExtractFirstFrame);
68
+ const frames = codec.composite(decoded).map(({ pixels, delay }) => {
69
+ const frame = new Frame(decoded.width, decoded.height, 10 * delay, 0, 0, Frame.DISPOSAL_BACKGROUND);
70
+ frame.bitmap.set(pixels);
71
+ return frame;
72
+ });
73
+ return new GIF(frames, decoded.loopCount);
74
+ }
75
+ resize(width, height, mode = Image.RESIZE_BILINEAR) {
76
+ const originalWidth = this.width, originalHeight = this.height;
77
+ if (width === Image.RESIZE_AUTO && height === Image.RESIZE_AUTO)
78
+ throw new Error('RESIZE_AUTO can only be used for either width or height, not for both');
79
+ if (width === Image.RESIZE_AUTO)
80
+ width = originalWidth / originalHeight * height;
81
+ if (height === Image.RESIZE_AUTO)
82
+ height = originalHeight / originalWidth * width;
83
+ const sx = width / originalWidth, sy = height / originalHeight;
84
+ for (const frame of this) {
85
+ const left = Math.round(frame.xOffset * sx), top = Math.round(frame.yOffset * sy);
86
+ const right = Math.round((frame.xOffset + frame.width) * sx), bottom = Math.round((frame.yOffset + frame.height) * sy);
87
+ Image.prototype.resize.call(frame, Math.max(1, right - left), Math.max(1, bottom - top), mode);
88
+ frame.xOffset = left;
89
+ frame.yOffset = top;
90
+ }
91
+ return this;
92
+ }
93
+ }
@@ -0,0 +1,20 @@
1
+ import { BinaryData } from './util/mem.js';
2
+ /** Image formats recognized by {@link ImageType.getType} */
3
+ export type ImageTypeName = 'png' | 'jpeg' | 'tiff' | 'gif';
4
+ /** Detects image formats by their magic numbers */
5
+ export declare class ImageType {
6
+ /**
7
+ * Gets an image's type from its magic number
8
+ * @param data The image binary to get the type of
9
+ * @returns The image type, or null if it isn't supported
10
+ */
11
+ static getType(data: BinaryData): ImageTypeName | null;
12
+ /** Whether the data starts like a PNG */
13
+ static isPNG(data: BinaryData): boolean;
14
+ /** Whether the data starts like a JPEG */
15
+ static isJPEG(data: BinaryData): boolean;
16
+ /** Whether the data starts like a TIFF (or BigTIFF) */
17
+ static isTIFF(data: BinaryData): boolean;
18
+ /** Whether the data starts like a GIF */
19
+ static isGIF(data: BinaryData): boolean;
20
+ }
@@ -0,0 +1,49 @@
1
+ import { view as bytes } from "./util/mem.js";
2
+ const MAGIC_NUMBERS = {
3
+ PNG: 0x89504e47,
4
+ JPEG: 0xffd8ff,
5
+ TIFF_LE: 0x49492a00,
6
+ TIFF_BE: 0x4d4d002a,
7
+ BIGTIFF_LE: 0x49492b00,
8
+ BIGTIFF_BE: 0x4d4d002b,
9
+ GIF: 0x474946
10
+ };
11
+ function dataView(data) {
12
+ if (data instanceof DataView)
13
+ return data;
14
+ const u8 = bytes(data);
15
+ return new DataView(u8.buffer, u8.byteOffset, u8.byteLength);
16
+ }
17
+ export class ImageType {
18
+ static getType(data) {
19
+ const view = dataView(data);
20
+ if (this.isPNG(view))
21
+ return 'png';
22
+ if (this.isJPEG(view))
23
+ return 'jpeg';
24
+ if (this.isTIFF(view))
25
+ return 'tiff';
26
+ if (this.isGIF(view))
27
+ return 'gif';
28
+ return null;
29
+ }
30
+ static isPNG(data) {
31
+ const view = dataView(data);
32
+ return view.byteLength >= 4 && view.getUint32(0, false) === MAGIC_NUMBERS.PNG;
33
+ }
34
+ static isJPEG(data) {
35
+ const view = dataView(data);
36
+ return view.byteLength >= 4 && (view.getUint32(0, false) >>> 8) === MAGIC_NUMBERS.JPEG;
37
+ }
38
+ static isTIFF(data) {
39
+ const view = dataView(data);
40
+ if (view.byteLength < 4)
41
+ return false;
42
+ const magic = view.getUint32(0, false);
43
+ return magic === MAGIC_NUMBERS.TIFF_LE || magic === MAGIC_NUMBERS.TIFF_BE || magic === MAGIC_NUMBERS.BIGTIFF_LE || magic === MAGIC_NUMBERS.BIGTIFF_BE;
44
+ }
45
+ static isGIF(data) {
46
+ const view = dataView(data);
47
+ return view.byteLength >= 4 && (view.getUint32(0, false) >>> 8) === MAGIC_NUMBERS.GIF;
48
+ }
49
+ }