image-to-pixel 3.0.1 → 3.2.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/package.json CHANGED
@@ -1,92 +1,92 @@
1
- {
2
- "name": "image-to-pixel",
3
- "version": "3.0.1",
4
- "description": "Turn images and video into pixel art: dithering, palettes, batch and video. A WebGL engine (via ShaderMate) with a CPU fallback, plus a full editor.",
5
- "type": "module",
6
- "license": "MIT",
7
- "author": "Tezumie",
8
- "homepage": "https://tezumie.github.io/Image-to-Pixel/",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/Tezumie/Image-to-Pixel.git"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/Tezumie/Image-to-Pixel/issues"
15
- },
16
- "keywords": [
17
- "pixel-art",
18
- "dithering",
19
- "palette",
20
- "quantization",
21
- "floyd-steinberg",
22
- "bayer",
23
- "webgl",
24
- "glsl",
25
- "shader",
26
- "gif",
27
- "sprite-sheet",
28
- "lospec"
29
- ],
30
- "main": "./dist/image-to-pixel.cjs",
31
- "module": "./dist/image-to-pixel.esm.js",
32
- "types": "./types/index.d.ts",
33
- "unpkg": "./dist/image-to-pixel.min.js",
34
- "jsdelivr": "./dist/image-to-pixel.min.js",
35
- "exports": {
36
- ".": {
37
- "types": "./types/index.d.ts",
38
- "import": "./dist/image-to-pixel.esm.js",
39
- "require": "./dist/image-to-pixel.cjs",
40
- "default": "./dist/image-to-pixel.esm.js"
41
- },
42
- "./video": {
43
- "types": "./types/video.d.ts",
44
- "import": "./dist/video.esm.js",
45
- "default": "./dist/video.esm.js"
46
- },
47
- "./editor": {
48
- "types": "./types/editor.d.ts",
49
- "import": "./dist/editor.esm.js",
50
- "default": "./dist/editor.esm.js"
51
- },
52
- "./editor.css": "./dist/editor.css",
53
- "./dist/*": "./dist/*",
54
- "./package.json": "./package.json"
55
- },
56
- "sideEffects": [
57
- "./dist/editor.css",
58
- "./dist/image-to-pixel.js",
59
- "./dist/image-to-pixel.min.js"
60
- ],
61
- "files": [
62
- "dist",
63
- "lib",
64
- "types",
65
- "LICENSE",
66
- "README.md"
67
- ],
68
- "engines": {
69
- "node": ">=18"
70
- },
71
- "scripts": {
72
- "build": "node esbuild.config.mjs",
73
- "build:site": "npm run build && node scripts/build-site.mjs",
74
- "watch": "node esbuild.config.mjs --watch",
75
- "dev": "node scripts/serve.mjs --dev",
76
- "preview": "node scripts/serve.mjs",
77
- "bluenoise": "node scripts/gen-bluenoise.mjs",
78
- "screenshots": "node scripts/screenshots.mjs",
79
- "clean": "node scripts/clean.mjs",
80
- "test": "npm run build:site && node --test",
81
- "prepublishOnly": "npm test",
82
- "check:runtime": "node scripts/nodepod-check.mjs"
83
- },
84
- "dependencies": {
85
- "shadermate": "^2.0.0"
86
- },
87
- "devDependencies": {
88
- "esbuild": "^0.25.12",
89
- "highlight.js": "^11.12.0",
90
- "marked": "^15.0.12"
91
- }
92
- }
1
+ {
2
+ "name": "image-to-pixel",
3
+ "version": "3.2.0",
4
+ "description": "Turn images and video into pixel art: dithering, palettes, batch and video. A WebGL engine (via ShaderMate) with a CPU fallback, plus a full editor.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Tezumie",
8
+ "homepage": "https://tezumie.github.io/Image-to-Pixel/",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Tezumie/Image-to-Pixel.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Tezumie/Image-to-Pixel/issues"
15
+ },
16
+ "keywords": [
17
+ "pixel-art",
18
+ "dithering",
19
+ "palette",
20
+ "quantization",
21
+ "floyd-steinberg",
22
+ "bayer",
23
+ "webgl",
24
+ "glsl",
25
+ "shader",
26
+ "gif",
27
+ "sprite-sheet",
28
+ "lospec"
29
+ ],
30
+ "main": "./dist/image-to-pixel.cjs",
31
+ "module": "./dist/image-to-pixel.esm.js",
32
+ "types": "./types/index.d.ts",
33
+ "unpkg": "./dist/image-to-pixel.min.js",
34
+ "jsdelivr": "./dist/image-to-pixel.min.js",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./types/index.d.ts",
38
+ "import": "./dist/image-to-pixel.esm.js",
39
+ "require": "./dist/image-to-pixel.cjs",
40
+ "default": "./dist/image-to-pixel.esm.js"
41
+ },
42
+ "./video": {
43
+ "types": "./types/video.d.ts",
44
+ "import": "./dist/video.esm.js",
45
+ "default": "./dist/video.esm.js"
46
+ },
47
+ "./editor": {
48
+ "types": "./types/editor.d.ts",
49
+ "import": "./dist/editor.esm.js",
50
+ "default": "./dist/editor.esm.js"
51
+ },
52
+ "./editor.css": "./dist/editor.css",
53
+ "./dist/*": "./dist/*",
54
+ "./package.json": "./package.json"
55
+ },
56
+ "sideEffects": [
57
+ "./dist/editor.css",
58
+ "./dist/image-to-pixel.js",
59
+ "./dist/image-to-pixel.min.js"
60
+ ],
61
+ "files": [
62
+ "dist",
63
+ "lib",
64
+ "types",
65
+ "LICENSE",
66
+ "README.md"
67
+ ],
68
+ "engines": {
69
+ "node": ">=18"
70
+ },
71
+ "scripts": {
72
+ "build": "node esbuild.config.mjs",
73
+ "build:site": "npm run build && node scripts/build-site.mjs",
74
+ "watch": "node esbuild.config.mjs --watch",
75
+ "dev": "node scripts/serve.mjs --dev",
76
+ "preview": "node scripts/serve.mjs",
77
+ "bluenoise": "node scripts/gen-bluenoise.mjs",
78
+ "screenshots": "node scripts/screenshots.mjs",
79
+ "clean": "node scripts/clean.mjs",
80
+ "test": "npm run build:site && node --test",
81
+ "prepublishOnly": "npm test",
82
+ "check:runtime": "node scripts/nodepod-check.mjs"
83
+ },
84
+ "dependencies": {
85
+ "shadermate": "^2.0.0"
86
+ },
87
+ "devDependencies": {
88
+ "esbuild": "^0.25.12",
89
+ "highlight.js": "^11.12.0",
90
+ "marked": "^15.0.12"
91
+ }
92
+ }
package/types/index.d.ts CHANGED
@@ -192,5 +192,7 @@ export function estimateFps(video: HTMLVideoElement, fallback?: number): Promise
192
192
  export function frameTimes(opts: { duration: number; fps: number; start?: number; end?: number; maxFrames?: number }): number[];
193
193
  export function forEachFrame(video: HTMLVideoElement, opts: {
194
194
  fps: number; start?: number; end?: number; maxFrames?: number; signal?: AbortSignal;
195
+ /** Explicit frame timestamps (seconds), in selection order. */
196
+ times?: number[];
195
197
  onFrame: (info: { index: number; time: number; total: number }) => unknown;
196
198
  }): Promise<number>;
package/types/video.d.ts CHANGED
@@ -1,43 +1,99 @@
1
- // image-to-pixel/video
2
- import type { RGBAImage } from './index';
3
- export { forEachFrame, estimateFps, seekTo, frameTimes, videoReady } from './index';
4
-
5
- export interface VideoEncoderOptions {
6
- format: 'mp4' | 'webm';
7
- fps: number;
8
- /** Size of the frames you will add. */
9
- width: number;
10
- height: number;
11
- /** Whole-number upscale applied to every frame. */
12
- scale?: number;
13
- bitrate?: number;
14
- }
15
- export interface PixelVideoEncoder {
16
- readonly width: number;
17
- readonly height: number;
18
- readonly codec: string;
19
- add(frame: RGBAImage): Promise<void>;
20
- finish(): Promise<Blob>;
21
- }
22
- export function createVideoEncoder(opts: VideoEncoderOptions): Promise<PixelVideoEncoder>;
23
- export function encodeVideo(frames: RGBAImage[], opts: Omit<VideoEncoderOptions, 'width' | 'height'>): Promise<Blob>;
24
- export function videoSupport(width?: number, height?: number): Promise<{ mp4: boolean; webm: boolean; webcodecs: boolean }>;
25
-
26
- /** Minimal MP4 muxer (one H.264 track, fast start). Feed it VideoEncoder output, or AVCC samples. */
27
- export interface Mp4Muxer {
28
- addChunk(chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata): void;
29
- /** timestamp/duration in microseconds; data is length-prefixed NAL units. */
30
- addSample(data: Uint8Array, info: { key: boolean; timestamp: number; duration?: number }): void;
31
- /** AVCDecoderConfigurationRecord ('avcC'). */
32
- setDescription(avcC: Uint8Array): void;
33
- finish(): Uint8Array;
34
- }
35
- export function createMp4Muxer(opts: { width: number; height: number; timescale?: number }): Mp4Muxer;
36
- /** Minimal WebM muxer (one VP8/VP9/AV1 track, with SeekHead and Cues). */
37
- export interface WebmMuxer {
38
- addChunk(chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata): void;
39
- /** timestamp in microseconds */
40
- addFrame(data: Uint8Array, info: { key: boolean; timestamp: number }): void;
41
- finish(): Uint8Array;
42
- }
43
- export function createWebmMuxer(opts: { width: number; height: number; codec?: 'vp8' | 'vp9' | 'av1'; fps?: number }): WebmMuxer;
1
+ // image-to-pixel/video
2
+ import type { RGBAImage } from './index';
3
+ export { forEachFrame, estimateFps, seekTo, frameTimes, videoReady } from './index';
4
+
5
+ export interface VideoEncoderOptions {
6
+ format: 'mp4' | 'webm';
7
+ fps: number;
8
+ /** Size of the frames you will add. */
9
+ width: number;
10
+ height: number;
11
+ /** Whole-number upscale applied to every frame. */
12
+ scale?: number;
13
+ bitrate?: number;
14
+ /**
15
+ * Soundtrack to include, e.g. from decodeAudio(file, { start, end }). Cut to the video's length.
16
+ * MP4 gets AAC (or Opus where AAC can't be encoded), WebM gets Opus. If the browser can't encode
17
+ * either, the video is made without it: check `audioCodec` on the encoder.
18
+ */
19
+ audio?: PCMAudio | null;
20
+ /** Bits per second for the audio. Default 128000. */
21
+ audioBitrate?: number;
22
+ }
23
+ /** Planar 32-bit float PCM, one array per channel. */
24
+ export interface PCMAudio {
25
+ sampleRate: number;
26
+ numberOfChannels: number;
27
+ channels: Float32Array[];
28
+ /** Seconds (informational). */
29
+ duration?: number;
30
+ }
31
+ export type AudioCodec = 'aac' | 'opus';
32
+ export interface PixelVideoEncoder {
33
+ readonly width: number;
34
+ readonly height: number;
35
+ readonly codec: string;
36
+ /** The audio codec used, or null when there is no soundtrack (none given, or none this browser can encode). */
37
+ readonly audioCodec: AudioCodec | null;
38
+ add(frame: RGBAImage): Promise<void>;
39
+ finish(): Promise<Blob>;
40
+ }
41
+ export function createVideoEncoder(opts: VideoEncoderOptions): Promise<PixelVideoEncoder>;
42
+ export function encodeVideo(frames: RGBAImage[], opts: Omit<VideoEncoderOptions, 'width' | 'height'>): Promise<Blob>;
43
+ export function videoSupport(width?: number, height?: number): Promise<{ mp4: boolean; webm: boolean; webcodecs: boolean; aac: boolean; opus: boolean }>;
44
+
45
+ /**
46
+ * Decode a file's audio track to PCM (resampled, default 48 kHz; mixed down to at most 2 channels; trimmed to
47
+ * start/end in seconds). Resolves with null when there is no audio track, it can't be decoded, or there is no
48
+ * Web Audio (Node).
49
+ */
50
+ export function decodeAudio(
51
+ source: Blob | ArrayBuffer | ArrayBufferView | string | HTMLMediaElement,
52
+ opts?: { start?: number; end?: number; sampleRate?: number; channels?: 1 | 2 },
53
+ ): Promise<PCMAudio | null>;
54
+ /** Encode PCM with WebCodecs' AudioEncoder; chunks go to onChunk (e.g. a muxer's addAudioChunk). */
55
+ export function encodeAudio(
56
+ pcm: PCMAudio,
57
+ opts: { codec?: AudioCodec; config?: AudioEncoderConfig; bitrate?: number; maxDuration?: number; onChunk: (chunk: EncodedAudioChunk, meta?: EncodedAudioChunkMetadata) => void },
58
+ ): Promise<void>;
59
+ export function audioCodecSupported(codec: AudioCodec, sampleRate?: number, numberOfChannels?: number): Promise<boolean>;
60
+ /** An AudioEncoder configuration this browser accepts for `codec` (common rates and bitrates are tried), or null. */
61
+ export function findAudioConfig(codec: AudioCodec, sampleRate?: number, numberOfChannels?: number, bitrate?: number): Promise<AudioEncoderConfig | null>;
62
+
63
+ /** The audio track a muxer should add. */
64
+ export interface MuxerAudioTrack {
65
+ codec: AudioCodec;
66
+ sampleRate: number;
67
+ numberOfChannels: number;
68
+ /** MP4 only: bitrate written into the AAC decoder config. */
69
+ bitrate?: number;
70
+ }
71
+
72
+ /** Minimal MP4 muxer (one H.264 track and an optional AAC or Opus track, fast start). Feed it encoder output, or raw samples. */
73
+ export interface Mp4Muxer {
74
+ addChunk(chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata): void;
75
+ /** timestamp/duration in microseconds; data is length-prefixed NAL units. */
76
+ addSample(data: Uint8Array, info: { key: boolean; timestamp: number; duration?: number }): void;
77
+ /** AVCDecoderConfigurationRecord ('avcC'). */
78
+ setDescription(avcC: Uint8Array): void;
79
+ /** AudioEncoder output (needs the `audio` option). */
80
+ addAudioChunk(chunk: EncodedAudioChunk, meta?: EncodedAudioChunkMetadata): void;
81
+ /** One encoded audio frame, in order. timestamp/duration in microseconds. */
82
+ addAudioSample(data: Uint8Array, info: { timestamp: number; duration?: number }): void;
83
+ /** AAC AudioSpecificConfig (for Opus, an optional OpusHead to read the pre-skip from). */
84
+ setAudioDescription(config: Uint8Array): void;
85
+ finish(): Uint8Array;
86
+ }
87
+ export function createMp4Muxer(opts: { width: number; height: number; timescale?: number; audio?: MuxerAudioTrack | null }): Mp4Muxer;
88
+ /** Minimal WebM muxer (one VP8/VP9/AV1 track and an optional Opus track, with SeekHead and Cues). */
89
+ export interface WebmMuxer {
90
+ addChunk(chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata): void;
91
+ /** timestamp in microseconds */
92
+ addFrame(data: Uint8Array, info: { key: boolean; timestamp: number }): void;
93
+ /** AudioEncoder output (needs the `audio` option). */
94
+ addAudioChunk(chunk: EncodedAudioChunk, meta?: EncodedAudioChunkMetadata): void;
95
+ /** One Opus packet, in order. timestamp/duration in microseconds. */
96
+ addAudioFrame(data: Uint8Array, info: { timestamp: number; duration?: number }): void;
97
+ finish(): Uint8Array;
98
+ }
99
+ export function createWebmMuxer(opts: { width: number; height: number; codec?: 'vp8' | 'vp9' | 'av1'; fps?: number; audio?: (MuxerAudioTrack & { codec: 'opus' }) | null }): WebmMuxer;