webcodecs-node 0.5.2 → 0.7.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/README.md +279 -0
- package/dist/backends/types.d.ts +2 -0
- package/dist/backends/types.d.ts.map +1 -1
- package/dist/backends/types.js.map +1 -1
- package/dist/canvas/canvas-utils.d.ts +115 -0
- package/dist/canvas/canvas-utils.d.ts.map +1 -0
- package/dist/canvas/canvas-utils.js +169 -0
- package/dist/canvas/canvas-utils.js.map +1 -0
- package/dist/canvas/frame-loop.d.ts +113 -0
- package/dist/canvas/frame-loop.d.ts.map +1 -0
- package/dist/canvas/frame-loop.js +291 -0
- package/dist/canvas/frame-loop.js.map +1 -0
- package/dist/canvas/gpu-context.d.ts +61 -0
- package/dist/canvas/gpu-context.d.ts.map +1 -0
- package/dist/canvas/gpu-context.js +134 -0
- package/dist/canvas/gpu-context.js.map +1 -0
- package/dist/canvas/index.d.ts +22 -0
- package/dist/canvas/index.d.ts.map +1 -0
- package/dist/canvas/index.js +25 -0
- package/dist/canvas/index.js.map +1 -0
- package/dist/canvas/types.d.ts +101 -0
- package/dist/canvas/types.d.ts.map +1 -0
- package/dist/canvas/types.js +7 -0
- package/dist/canvas/types.js.map +1 -0
- package/dist/codec-utils/formats.d.ts.map +1 -1
- package/dist/codec-utils/formats.js +31 -0
- package/dist/codec-utils/formats.js.map +1 -1
- package/dist/config/ffmpeg-quality.d.ts +14 -0
- package/dist/config/ffmpeg-quality.d.ts.map +1 -0
- package/dist/config/ffmpeg-quality.js +41 -0
- package/dist/config/ffmpeg-quality.js.map +1 -0
- package/dist/containers/Muxer.d.ts.map +1 -1
- package/dist/containers/Muxer.js +4 -1
- package/dist/containers/Muxer.js.map +1 -1
- package/dist/core/AudioData.d.ts +2 -1
- package/dist/core/AudioData.d.ts.map +1 -1
- package/dist/core/AudioData.js.map +1 -1
- package/dist/core/VideoFrame.d.ts +2 -2
- package/dist/core/VideoFrame.d.ts.map +1 -1
- package/dist/core/VideoFrame.js +49 -47
- package/dist/core/VideoFrame.js.map +1 -1
- package/dist/decoders/AudioDecoder.d.ts +1 -0
- package/dist/decoders/AudioDecoder.d.ts.map +1 -1
- package/dist/decoders/AudioDecoder.js +39 -17
- package/dist/decoders/AudioDecoder.js.map +1 -1
- package/dist/decoders/ImageDecoder.d.ts +1 -0
- package/dist/decoders/ImageDecoder.d.ts.map +1 -1
- package/dist/decoders/ImageDecoder.js +40 -3
- package/dist/decoders/ImageDecoder.js.map +1 -1
- package/dist/decoders/VideoDecoder.d.ts +12 -0
- package/dist/decoders/VideoDecoder.d.ts.map +1 -1
- package/dist/decoders/VideoDecoder.js +50 -8
- package/dist/decoders/VideoDecoder.js.map +1 -1
- package/dist/demos/demo-1080p-transcode.js +8 -2
- package/dist/demos/demo-1080p-transcode.js.map +1 -1
- package/dist/demos/demo-audio-visualizer.d.ts +11 -0
- package/dist/demos/demo-audio-visualizer.d.ts.map +1 -0
- package/dist/demos/demo-audio-visualizer.js +281 -0
- package/dist/demos/demo-audio-visualizer.js.map +1 -0
- package/dist/demos/demo-dvd-logo.d.ts +8 -0
- package/dist/demos/demo-dvd-logo.d.ts.map +1 -0
- package/dist/demos/demo-dvd-logo.js +196 -0
- package/dist/demos/demo-dvd-logo.js.map +1 -0
- package/dist/demos/demo-four-corners.js +9 -0
- package/dist/demos/demo-four-corners.js.map +1 -1
- package/dist/demos/demo-streaming.js +6 -0
- package/dist/demos/demo-streaming.js.map +1 -1
- package/dist/demos/demo-webcodecs.js +1 -0
- package/dist/demos/demo-webcodecs.js.map +1 -1
- package/dist/encoders/AudioEncoder.d.ts +3 -0
- package/dist/encoders/AudioEncoder.d.ts.map +1 -1
- package/dist/encoders/AudioEncoder.js +70 -28
- package/dist/encoders/AudioEncoder.js.map +1 -1
- package/dist/encoders/ImageEncoder.d.ts +80 -0
- package/dist/encoders/ImageEncoder.d.ts.map +1 -0
- package/dist/encoders/ImageEncoder.js +156 -0
- package/dist/encoders/ImageEncoder.js.map +1 -0
- package/dist/encoders/VideoEncoder.d.ts +11 -0
- package/dist/encoders/VideoEncoder.d.ts.map +1 -1
- package/dist/encoders/VideoEncoder.js +46 -4
- package/dist/encoders/VideoEncoder.js.map +1 -1
- package/dist/encoders/index.d.ts +1 -0
- package/dist/encoders/index.d.ts.map +1 -1
- package/dist/encoders/index.js +1 -0
- package/dist/encoders/index.js.map +1 -1
- package/dist/formats/color-space.d.ts +88 -0
- package/dist/formats/color-space.d.ts.map +1 -1
- package/dist/formats/color-space.js +55 -1
- package/dist/formats/color-space.js.map +1 -1
- package/dist/formats/pixel-formats.d.ts +17 -1
- package/dist/formats/pixel-formats.d.ts.map +1 -1
- package/dist/formats/pixel-formats.js +74 -4
- package/dist/formats/pixel-formats.js.map +1 -1
- package/dist/hardware/detection.d.ts.map +1 -1
- package/dist/hardware/detection.js +5 -2
- package/dist/hardware/detection.js.map +1 -1
- package/dist/hardware/encoder-args.d.ts.map +1 -1
- package/dist/hardware/encoder-args.js +6 -6
- package/dist/hardware/encoder-args.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/node-av/NodeAvAudioDecoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvAudioDecoder.js +3 -0
- package/dist/node-av/NodeAvAudioDecoder.js.map +1 -1
- package/dist/node-av/NodeAvAudioEncoder.d.ts +5 -0
- package/dist/node-av/NodeAvAudioEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvAudioEncoder.js +102 -9
- package/dist/node-av/NodeAvAudioEncoder.js.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.d.ts +2 -0
- package/dist/node-av/NodeAvVideoEncoder.d.ts.map +1 -1
- package/dist/node-av/NodeAvVideoEncoder.js +44 -6
- package/dist/node-av/NodeAvVideoEncoder.js.map +1 -1
- package/dist/node-av/WebPImageDecoder.d.ts +54 -0
- package/dist/node-av/WebPImageDecoder.d.ts.map +1 -0
- package/dist/node-av/WebPImageDecoder.js +176 -0
- package/dist/node-av/WebPImageDecoder.js.map +1 -0
- package/dist/polyfills/OffscreenCanvas.d.ts +141 -7
- package/dist/polyfills/OffscreenCanvas.d.ts.map +1 -1
- package/dist/polyfills/OffscreenCanvas.js +217 -17
- package/dist/polyfills/OffscreenCanvas.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/native-frame.d.ts +74 -0
- package/dist/types/native-frame.d.ts.map +1 -0
- package/dist/types/native-frame.js +32 -0
- package/dist/types/native-frame.js.map +1 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/type-guards.d.ts +29 -1
- package/dist/utils/type-guards.d.ts.map +1 -1
- package/dist/utils/type-guards.js +47 -2
- package/dist/utils/type-guards.js.map +1 -1
- package/docs/api.md +155 -0
- package/docs/configuration.md +27 -0
- package/examples/README.md +28 -1
- package/examples/canvas-encoding.ts +222 -0
- package/examples/offscreen-canvas.ts +230 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ This package provides a Node.js-compatible implementation of the [WebCodecs API]
|
|
|
9
9
|
- **VideoEncoder / VideoDecoder** - H.264, HEVC, VP8, VP9, AV1
|
|
10
10
|
- **AudioEncoder / AudioDecoder** - AAC, Opus, MP3, FLAC, Vorbis
|
|
11
11
|
- **ImageDecoder** - PNG, JPEG, WebP, GIF, AVIF, BMP, TIFF (including animated with frame timing)
|
|
12
|
+
- **ImageEncoder** - Encode VideoFrames to PNG, JPEG, WebP
|
|
12
13
|
- **VideoFrame / AudioData** - Frame-level data manipulation
|
|
13
14
|
- **MediaCapabilities** - Query codec support, smooth playback, and power efficiency
|
|
14
15
|
- **Hardware Acceleration** - VAAPI, NVENC, QSV support
|
|
@@ -16,6 +17,7 @@ This package provides a Node.js-compatible implementation of the [WebCodecs API]
|
|
|
16
17
|
- **Latency Modes** - Configure for real-time streaming vs maximum compression
|
|
17
18
|
- **Bitrate Modes** - Constant, variable, and quantizer (CRF) encoding modes
|
|
18
19
|
- **Alpha Channel** - Preserve transparency with VP9 and AV1 codecs
|
|
20
|
+
- **10-bit & HDR** - I420P10, P010 formats with HDR10 metadata support
|
|
19
21
|
- **Container Support** - MP4, WebM demuxing/muxing utilities
|
|
20
22
|
|
|
21
23
|
## Documentation
|
|
@@ -246,6 +248,36 @@ decoder.close();
|
|
|
246
248
|
- `image/bmp`
|
|
247
249
|
- `image/tiff`
|
|
248
250
|
|
|
251
|
+
### ImageEncoder
|
|
252
|
+
|
|
253
|
+
Encodes VideoFrames to image formats (PNG, JPEG, WebP). This is a utility class that mirrors ImageDecoder.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { ImageEncoder, VideoFrame } from 'webcodecs-node';
|
|
257
|
+
|
|
258
|
+
// Check format support
|
|
259
|
+
ImageEncoder.isTypeSupported('image/webp'); // true
|
|
260
|
+
|
|
261
|
+
// Encode a frame to JPEG
|
|
262
|
+
const result = await ImageEncoder.encode(frame, {
|
|
263
|
+
type: 'image/jpeg',
|
|
264
|
+
quality: 0.85,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
fs.writeFileSync('output.jpg', Buffer.from(result.data));
|
|
268
|
+
|
|
269
|
+
// Synchronous encoding
|
|
270
|
+
const pngResult = ImageEncoder.encodeSync(frame, { type: 'image/png' });
|
|
271
|
+
|
|
272
|
+
// Batch encode multiple frames
|
|
273
|
+
const results = await ImageEncoder.encodeBatch(frames, { type: 'image/webp' });
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Supported output formats:**
|
|
277
|
+
- `image/png` - Lossless, supports transparency
|
|
278
|
+
- `image/jpeg` - Lossy, quality 0-1 (default: 0.92)
|
|
279
|
+
- `image/webp` - Lossy/lossless, quality 0-1 (default: 0.8)
|
|
280
|
+
|
|
249
281
|
### MediaCapabilities API
|
|
250
282
|
|
|
251
283
|
Query codec capabilities before encoding/decoding. Implements the standard [MediaCapabilities API](https://developer.mozilla.org/en-US/docs/Web/API/MediaCapabilities).
|
|
@@ -419,6 +451,207 @@ const frame = new VideoFrame(rgbaWithAlpha, {
|
|
|
419
451
|
encoder.encode(frame);
|
|
420
452
|
```
|
|
421
453
|
|
|
454
|
+
### 10-bit Pixel Formats & HDR
|
|
455
|
+
|
|
456
|
+
Support for high bit-depth content and HDR metadata:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
import {
|
|
460
|
+
VideoFrame,
|
|
461
|
+
VideoColorSpace,
|
|
462
|
+
createHdr10MasteringMetadata,
|
|
463
|
+
createContentLightLevel,
|
|
464
|
+
is10BitFormat,
|
|
465
|
+
getBitDepth,
|
|
466
|
+
} from 'webcodecs-node';
|
|
467
|
+
|
|
468
|
+
// Create a 10-bit frame
|
|
469
|
+
const frame = new VideoFrame(yuv10bitData, {
|
|
470
|
+
format: 'I420P10', // 10-bit YUV 4:2:0
|
|
471
|
+
codedWidth: 3840,
|
|
472
|
+
codedHeight: 2160,
|
|
473
|
+
timestamp: 0,
|
|
474
|
+
colorSpace: new VideoColorSpace({
|
|
475
|
+
primaries: 'bt2020',
|
|
476
|
+
transfer: 'pq', // HDR10 PQ transfer
|
|
477
|
+
matrix: 'bt2020-ncl',
|
|
478
|
+
}),
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Check format properties
|
|
482
|
+
console.log(is10BitFormat('I420P10')); // true
|
|
483
|
+
console.log(getBitDepth('I420P10')); // 10
|
|
484
|
+
|
|
485
|
+
// HDR metadata for mastering display
|
|
486
|
+
const hdrMetadata = {
|
|
487
|
+
smpteSt2086: createHdr10MasteringMetadata(1000, 0.0001), // max/min luminance
|
|
488
|
+
contentLightLevel: createContentLightLevel(800, 400), // MaxCLL, MaxFALL
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const colorSpace = new VideoColorSpace({
|
|
492
|
+
primaries: 'bt2020',
|
|
493
|
+
transfer: 'pq',
|
|
494
|
+
hdrMetadata,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
console.log(colorSpace.isHdr); // true
|
|
498
|
+
console.log(colorSpace.hasHdrMetadata); // true
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**10-bit pixel formats:**
|
|
502
|
+
- `I420P10` - YUV 4:2:0 planar, 10-bit
|
|
503
|
+
- `I422P10` - YUV 4:2:2 planar, 10-bit
|
|
504
|
+
- `I444P10` - YUV 4:4:4 planar, 10-bit
|
|
505
|
+
- `P010` - YUV 4:2:0 semi-planar, 10-bit
|
|
506
|
+
|
|
507
|
+
**Pixel format utilities:**
|
|
508
|
+
- `is10BitFormat(format)` - Check if format is 10-bit
|
|
509
|
+
- `getBitDepth(format)` - Get bit depth (8 or 10)
|
|
510
|
+
- `get8BitEquivalent(format)` - Get 8-bit version of a 10-bit format
|
|
511
|
+
- `get10BitEquivalent(format)` - Get 10-bit version of an 8-bit format
|
|
512
|
+
|
|
513
|
+
### Canvas Rendering (skia-canvas)
|
|
514
|
+
|
|
515
|
+
GPU-accelerated 2D canvas rendering with automatic hardware detection:
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
import {
|
|
519
|
+
createCanvas,
|
|
520
|
+
createFrameLoop,
|
|
521
|
+
detectGpuAcceleration,
|
|
522
|
+
isGpuAvailable,
|
|
523
|
+
getGpuApi,
|
|
524
|
+
ensureEvenDimensions,
|
|
525
|
+
VideoEncoder,
|
|
526
|
+
} from 'webcodecs-node';
|
|
527
|
+
|
|
528
|
+
// Check GPU availability
|
|
529
|
+
const gpuInfo = detectGpuAcceleration();
|
|
530
|
+
console.log(`Renderer: ${gpuInfo.renderer}`); // 'GPU' or 'CPU'
|
|
531
|
+
console.log(`API: ${getGpuApi()}`); // 'Metal', 'Vulkan', 'D3D', or null
|
|
532
|
+
|
|
533
|
+
// Create GPU-accelerated canvas
|
|
534
|
+
const canvas = createCanvas({
|
|
535
|
+
width: 1920,
|
|
536
|
+
height: 1080,
|
|
537
|
+
gpu: true, // or omit for auto-detection
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
const ctx = canvas.getContext('2d');
|
|
541
|
+
ctx.fillStyle = 'red';
|
|
542
|
+
ctx.fillRect(0, 0, 1920, 1080);
|
|
543
|
+
|
|
544
|
+
// Create VideoFrame directly from canvas
|
|
545
|
+
const frame = new VideoFrame(canvas, { timestamp: 0 });
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**FrameLoop helper** for animation with backpressure:
|
|
549
|
+
|
|
550
|
+
```typescript
|
|
551
|
+
const loop = createFrameLoop({
|
|
552
|
+
width: 1920,
|
|
553
|
+
height: 1080,
|
|
554
|
+
frameRate: 30,
|
|
555
|
+
maxQueueSize: 8, // Backpressure limit
|
|
556
|
+
onFrame: (ctx, timing) => {
|
|
557
|
+
// Draw each frame
|
|
558
|
+
ctx.fillStyle = `hsl(${timing.frameIndex % 360}, 100%, 50%)`;
|
|
559
|
+
ctx.fillRect(0, 0, 1920, 1080);
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
loop.start(300); // Generate 300 frames
|
|
564
|
+
|
|
565
|
+
while (loop.getState() !== 'stopped' || loop.getQueueSize() > 0) {
|
|
566
|
+
const frame = loop.takeFrame();
|
|
567
|
+
if (frame) {
|
|
568
|
+
encoder.encode(frame);
|
|
569
|
+
frame.close(); // Always close frames!
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
**OffscreenCanvas polyfill** for browser-compatible code:
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
import { installOffscreenCanvasPolyfill } from 'webcodecs-node';
|
|
578
|
+
|
|
579
|
+
installOffscreenCanvasPolyfill();
|
|
580
|
+
|
|
581
|
+
// Now use standard OffscreenCanvas API
|
|
582
|
+
const canvas = new OffscreenCanvas(1920, 1080);
|
|
583
|
+
const ctx = canvas.getContext('2d');
|
|
584
|
+
const blob = await canvas.convertToBlob({ type: 'image/png' });
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
## Performance Tuning
|
|
588
|
+
|
|
589
|
+
### Memory Management
|
|
590
|
+
|
|
591
|
+
Always close VideoFrames and AudioData when done:
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
const frame = new VideoFrame(buffer, { ... });
|
|
595
|
+
try {
|
|
596
|
+
encoder.encode(frame);
|
|
597
|
+
} finally {
|
|
598
|
+
frame.close(); // Prevent memory leaks
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### Even Dimensions
|
|
603
|
+
|
|
604
|
+
Video codecs require even dimensions for YUV420 chroma subsampling:
|
|
605
|
+
|
|
606
|
+
```typescript
|
|
607
|
+
import { ensureEvenDimensions, validateEvenDimensions } from 'webcodecs-node';
|
|
608
|
+
|
|
609
|
+
// Auto-fix odd dimensions (rounds up)
|
|
610
|
+
const { width, height } = ensureEvenDimensions(1279, 719);
|
|
611
|
+
// Returns { width: 1280, height: 720 }
|
|
612
|
+
|
|
613
|
+
// Strict validation (throws if odd)
|
|
614
|
+
validateEvenDimensions(1280, 720); // OK
|
|
615
|
+
validateEvenDimensions(1279, 720); // Throws TypeError
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Backpressure Handling
|
|
619
|
+
|
|
620
|
+
Monitor encoder queue to prevent memory exhaustion:
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
encoder.addEventListener('dequeue', () => {
|
|
624
|
+
// Queue size decreased, safe to encode more
|
|
625
|
+
if (encoder.encodeQueueSize < 10) {
|
|
626
|
+
encodeNextFrame();
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Raw Buffer Export
|
|
632
|
+
|
|
633
|
+
For maximum performance, use raw RGBA buffers instead of PNG/JPEG:
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
import { getRawPixels } from 'webcodecs-node';
|
|
637
|
+
|
|
638
|
+
// Fast: raw RGBA buffer (no compression)
|
|
639
|
+
const pixels = getRawPixels(canvas); // Returns Buffer
|
|
640
|
+
|
|
641
|
+
// Slow: PNG encoding (avoid in hot paths)
|
|
642
|
+
const png = await canvas.toBuffer('png');
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### GPU vs CPU Tradeoffs
|
|
646
|
+
|
|
647
|
+
| Scenario | Recommendation |
|
|
648
|
+
|----------|----------------|
|
|
649
|
+
| HD/4K encoding | `hardwareAcceleration: 'prefer-hardware'` |
|
|
650
|
+
| Real-time streaming | Hardware + `latencyMode: 'realtime'` |
|
|
651
|
+
| Maximum quality | Software + `bitrateMode: 'quantizer'` |
|
|
652
|
+
| Batch processing | Hardware for throughput |
|
|
653
|
+
| Low-end systems | Software (more compatible) |
|
|
654
|
+
|
|
422
655
|
## Demos
|
|
423
656
|
|
|
424
657
|
Run the included demos to test functionality:
|
|
@@ -452,6 +685,52 @@ npm run demo:fourcorners
|
|
|
452
685
|
|
|
453
686
|
# 1080p transcoding demo
|
|
454
687
|
npm run demo:1080p
|
|
688
|
+
|
|
689
|
+
# DVD bouncing logo animation
|
|
690
|
+
npm run demo:dvd
|
|
691
|
+
|
|
692
|
+
# Audio visualizer with waveform and spectrum
|
|
693
|
+
npm run demo:visualizer
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
## Benchmarking
|
|
697
|
+
|
|
698
|
+
Compare software vs hardware encoding performance:
|
|
699
|
+
|
|
700
|
+
```bash
|
|
701
|
+
# Quick benchmark (30 frames, 360p)
|
|
702
|
+
npm run bench:quick
|
|
703
|
+
|
|
704
|
+
# Default benchmark (120 frames, 720p)
|
|
705
|
+
npm run bench
|
|
706
|
+
|
|
707
|
+
# Full benchmark (300 frames, 1080p)
|
|
708
|
+
npm run bench:full
|
|
709
|
+
|
|
710
|
+
# Custom options
|
|
711
|
+
node scripts/encoding-benchmark.mjs --frames 100 --resolution 1080p --codecs h264,hevc
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
**Options:**
|
|
715
|
+
- `--frames <n>` - Number of frames to encode (default: 120)
|
|
716
|
+
- `--resolution <res>` - 360p, 480p, 720p, 1080p, 4k (default: 720p)
|
|
717
|
+
- `--bitrate <bps>` - Target bitrate in bps
|
|
718
|
+
- `--framerate <fps>` - Target framerate (default: 30)
|
|
719
|
+
- `--codecs <list>` - Comma-separated: h264,hevc,vp9,av1
|
|
720
|
+
- `--skip-software` - Only test hardware encoding
|
|
721
|
+
- `--verbose` - Show detailed output
|
|
722
|
+
|
|
723
|
+
**Example output:**
|
|
724
|
+
```
|
|
725
|
+
════════════════════════════════════════════════════════════════════════════════
|
|
726
|
+
ENCODING BENCHMARK RESULTS (720p)
|
|
727
|
+
════════════════════════════════════════════════════════════════════════════════
|
|
728
|
+
Codec Mode FPS Time Latency Size Bitrate
|
|
729
|
+
────────────────────────────────────────────────────────────────────────────────
|
|
730
|
+
H.264/AVC SW 213.6 562ms 391ms 2.00 MB 4.20 Mbps
|
|
731
|
+
H.264/AVC HW 370.4 324ms 187ms 2.11 MB 4.43 Mbps
|
|
732
|
+
H.265/HEVC SW 141.4 848ms 106ms 1.94 MB 4.06 Mbps
|
|
733
|
+
H.265/HEVC HW 589.0 204ms 61ms 2.16 MB 4.54 Mbps
|
|
455
734
|
```
|
|
456
735
|
|
|
457
736
|
## API Compatibility
|
package/dist/backends/types.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface VideoEncoderBackendConfig {
|
|
|
40
40
|
latencyMode?: 'quality' | 'realtime';
|
|
41
41
|
alpha?: 'discard' | 'keep';
|
|
42
42
|
hardwareAcceleration?: 'no-preference' | 'prefer-hardware' | 'prefer-software';
|
|
43
|
+
/** Output format: 'annexb' for raw Annex B, 'mp4' for length-prefixed (AVCC/HVCC) */
|
|
44
|
+
format?: 'annexb' | 'mp4';
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
45
47
|
* Video decoder configuration
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;IACpD,WAAW,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACrC,KAAK,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,eAAe,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkLH,iDAAiD;AACjD,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C,gDAAgD;AAChD,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAE3C,wDAAwD;AACxD,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C,2CAA2C;AAC3C,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,2DAA2D;AAC3D,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C,4CAA4C;AAC5C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,EAAE;IACR,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;CACC,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas Utilities for skia-canvas
|
|
3
|
+
*
|
|
4
|
+
* Raw buffer utilities for efficient pixel data handling.
|
|
5
|
+
* Always uses toBuffer('raw') - never PNG or other encoded formats.
|
|
6
|
+
*/
|
|
7
|
+
import { Canvas } from 'skia-canvas';
|
|
8
|
+
import type { RawBufferOptions } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Create a pixel buffer for image manipulation
|
|
11
|
+
*
|
|
12
|
+
* Uses Uint8ClampedArray which automatically clamps values to 0-255,
|
|
13
|
+
* preventing overflow bugs in filters and pixel manipulation code.
|
|
14
|
+
*
|
|
15
|
+
* @param width - Image width in pixels
|
|
16
|
+
* @param height - Image height in pixels
|
|
17
|
+
* @returns Uint8ClampedArray of size width * height * 4 (RGBA)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const pixels = createPixelBuffer(1920, 1080);
|
|
22
|
+
* // Safe manipulation - values auto-clamped to 0-255
|
|
23
|
+
* pixels[0] = 300; // Becomes 255
|
|
24
|
+
* pixels[1] = -50; // Becomes 0
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function createPixelBuffer(width: number, height: number): Uint8ClampedArray;
|
|
28
|
+
/**
|
|
29
|
+
* Create a pixel buffer initialized with a solid color
|
|
30
|
+
*
|
|
31
|
+
* @param width - Image width in pixels
|
|
32
|
+
* @param height - Image height in pixels
|
|
33
|
+
* @param r - Red component (0-255)
|
|
34
|
+
* @param g - Green component (0-255)
|
|
35
|
+
* @param b - Blue component (0-255)
|
|
36
|
+
* @param a - Alpha component (0-255, default 255)
|
|
37
|
+
* @returns Uint8ClampedArray filled with the specified color
|
|
38
|
+
*/
|
|
39
|
+
export declare function createPixelBufferWithColor(width: number, height: number, r: number, g: number, b: number, a?: number): Uint8ClampedArray;
|
|
40
|
+
/**
|
|
41
|
+
* Get raw RGBA pixel data from canvas (synchronous)
|
|
42
|
+
* ALWAYS uses toBufferSync('raw') - never PNG or other encoded formats.
|
|
43
|
+
*
|
|
44
|
+
* @param canvas - The skia-canvas Canvas instance
|
|
45
|
+
* @param options - Optional buffer format options
|
|
46
|
+
* @returns Buffer containing raw RGBA pixel data
|
|
47
|
+
*/
|
|
48
|
+
export declare function getRawPixels(canvas: Canvas, options?: RawBufferOptions): Buffer;
|
|
49
|
+
/**
|
|
50
|
+
* Get raw RGBA pixel data from canvas (asynchronous)
|
|
51
|
+
* ALWAYS uses toBuffer('raw') - never PNG or other encoded formats.
|
|
52
|
+
*
|
|
53
|
+
* @param canvas - The skia-canvas Canvas instance
|
|
54
|
+
* @param options - Optional buffer format options
|
|
55
|
+
* @returns Promise resolving to Buffer containing raw RGBA pixel data
|
|
56
|
+
*/
|
|
57
|
+
export declare function getRawPixelsAsync(canvas: Canvas, options?: RawBufferOptions): Promise<Buffer>;
|
|
58
|
+
/**
|
|
59
|
+
* Reset canvas state for new frame
|
|
60
|
+
*
|
|
61
|
+
* Prevents Skia command history buildup which can cause memory
|
|
62
|
+
* growth and performance degradation over time.
|
|
63
|
+
*
|
|
64
|
+
* @param ctx - The 2D rendering context
|
|
65
|
+
*/
|
|
66
|
+
export declare function resetCanvas(ctx: CanvasRenderingContext2D): void;
|
|
67
|
+
/**
|
|
68
|
+
* Simple ImageData-like object for internal use
|
|
69
|
+
*/
|
|
70
|
+
export interface SimpleImageData {
|
|
71
|
+
data: Uint8ClampedArray;
|
|
72
|
+
width: number;
|
|
73
|
+
height: number;
|
|
74
|
+
colorSpace?: string;
|
|
75
|
+
colorType?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Convert Uint8Array/Buffer to ImageData-like object
|
|
79
|
+
* Compatible with skia-canvas putImageData
|
|
80
|
+
*
|
|
81
|
+
* @param data - Raw pixel data (RGBA format)
|
|
82
|
+
* @param width - Image width in pixels
|
|
83
|
+
* @param height - Image height in pixels
|
|
84
|
+
* @returns ImageData-like object
|
|
85
|
+
*/
|
|
86
|
+
export declare function pixelsToImageData(data: Uint8Array | Buffer, width: number, height: number): SimpleImageData;
|
|
87
|
+
/**
|
|
88
|
+
* Draw raw RGBA pixels to canvas
|
|
89
|
+
*
|
|
90
|
+
* @param canvas - The skia-canvas Canvas instance
|
|
91
|
+
* @param data - Raw pixel data (RGBA format)
|
|
92
|
+
* @param width - Image width in pixels
|
|
93
|
+
* @param height - Image height in pixels
|
|
94
|
+
*/
|
|
95
|
+
export declare function drawPixelsToCanvas(canvas: Canvas, data: Uint8Array | Buffer, width: number, height: number): void;
|
|
96
|
+
/**
|
|
97
|
+
* Create a Uint8Array view of a Buffer without copying
|
|
98
|
+
*
|
|
99
|
+
* @param buffer - Node.js Buffer
|
|
100
|
+
* @returns Uint8Array view of the same memory
|
|
101
|
+
*/
|
|
102
|
+
export declare function bufferToUint8Array(buffer: Buffer): Uint8Array;
|
|
103
|
+
/**
|
|
104
|
+
* Resize raw pixel data using canvas
|
|
105
|
+
*
|
|
106
|
+
* @param data - Source pixel data (RGBA format)
|
|
107
|
+
* @param srcWidth - Source width
|
|
108
|
+
* @param srcHeight - Source height
|
|
109
|
+
* @param dstWidth - Destination width
|
|
110
|
+
* @param dstHeight - Destination height
|
|
111
|
+
* @param canvas - Optional canvas to reuse (for performance)
|
|
112
|
+
* @returns Resized pixel data as Buffer
|
|
113
|
+
*/
|
|
114
|
+
export declare function resizePixels(data: Uint8Array | Buffer, srcWidth: number, srcHeight: number, dstWidth: number, dstHeight: number, canvas?: Canvas): Buffer;
|
|
115
|
+
//# sourceMappingURL=canvas-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-utils.d.ts","sourceRoot":"","sources":["../../src/canvas/canvas-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAElF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,GAAE,MAAY,GACd,iBAAiB,CASnB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAI/E;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,wBAAwB,GAAG,IAAI,CAW/D;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,eAAe,CAajB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,IAAI,CAIN;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAE7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,GAAG,MAAM,EACzB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAqBR"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas Utilities for skia-canvas
|
|
3
|
+
*
|
|
4
|
+
* Raw buffer utilities for efficient pixel data handling.
|
|
5
|
+
* Always uses toBuffer('raw') - never PNG or other encoded formats.
|
|
6
|
+
*/
|
|
7
|
+
import { Canvas } from 'skia-canvas';
|
|
8
|
+
/**
|
|
9
|
+
* Create a pixel buffer for image manipulation
|
|
10
|
+
*
|
|
11
|
+
* Uses Uint8ClampedArray which automatically clamps values to 0-255,
|
|
12
|
+
* preventing overflow bugs in filters and pixel manipulation code.
|
|
13
|
+
*
|
|
14
|
+
* @param width - Image width in pixels
|
|
15
|
+
* @param height - Image height in pixels
|
|
16
|
+
* @returns Uint8ClampedArray of size width * height * 4 (RGBA)
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const pixels = createPixelBuffer(1920, 1080);
|
|
21
|
+
* // Safe manipulation - values auto-clamped to 0-255
|
|
22
|
+
* pixels[0] = 300; // Becomes 255
|
|
23
|
+
* pixels[1] = -50; // Becomes 0
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function createPixelBuffer(width, height) {
|
|
27
|
+
return new Uint8ClampedArray(width * height * 4);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a pixel buffer initialized with a solid color
|
|
31
|
+
*
|
|
32
|
+
* @param width - Image width in pixels
|
|
33
|
+
* @param height - Image height in pixels
|
|
34
|
+
* @param r - Red component (0-255)
|
|
35
|
+
* @param g - Green component (0-255)
|
|
36
|
+
* @param b - Blue component (0-255)
|
|
37
|
+
* @param a - Alpha component (0-255, default 255)
|
|
38
|
+
* @returns Uint8ClampedArray filled with the specified color
|
|
39
|
+
*/
|
|
40
|
+
export function createPixelBufferWithColor(width, height, r, g, b, a = 255) {
|
|
41
|
+
const pixels = new Uint8ClampedArray(width * height * 4);
|
|
42
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
43
|
+
pixels[i] = r;
|
|
44
|
+
pixels[i + 1] = g;
|
|
45
|
+
pixels[i + 2] = b;
|
|
46
|
+
pixels[i + 3] = a;
|
|
47
|
+
}
|
|
48
|
+
return pixels;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get raw RGBA pixel data from canvas (synchronous)
|
|
52
|
+
* ALWAYS uses toBufferSync('raw') - never PNG or other encoded formats.
|
|
53
|
+
*
|
|
54
|
+
* @param canvas - The skia-canvas Canvas instance
|
|
55
|
+
* @param options - Optional buffer format options
|
|
56
|
+
* @returns Buffer containing raw RGBA pixel data
|
|
57
|
+
*/
|
|
58
|
+
export function getRawPixels(canvas, options) {
|
|
59
|
+
return canvas.toBufferSync('raw', {
|
|
60
|
+
colorType: options?.colorType ?? 'rgba',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get raw RGBA pixel data from canvas (asynchronous)
|
|
65
|
+
* ALWAYS uses toBuffer('raw') - never PNG or other encoded formats.
|
|
66
|
+
*
|
|
67
|
+
* @param canvas - The skia-canvas Canvas instance
|
|
68
|
+
* @param options - Optional buffer format options
|
|
69
|
+
* @returns Promise resolving to Buffer containing raw RGBA pixel data
|
|
70
|
+
*/
|
|
71
|
+
export async function getRawPixelsAsync(canvas, options) {
|
|
72
|
+
return canvas.toBuffer('raw', {
|
|
73
|
+
colorType: options?.colorType ?? 'rgba',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Reset canvas state for new frame
|
|
78
|
+
*
|
|
79
|
+
* Prevents Skia command history buildup which can cause memory
|
|
80
|
+
* growth and performance degradation over time.
|
|
81
|
+
*
|
|
82
|
+
* @param ctx - The 2D rendering context
|
|
83
|
+
*/
|
|
84
|
+
export function resetCanvas(ctx) {
|
|
85
|
+
// Use reset() if available (Canvas API), otherwise clearRect
|
|
86
|
+
if (typeof ctx.reset === 'function') {
|
|
87
|
+
ctx.reset();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const canvas = ctx.canvas;
|
|
91
|
+
ctx.save();
|
|
92
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
93
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
94
|
+
ctx.restore();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Convert Uint8Array/Buffer to ImageData-like object
|
|
99
|
+
* Compatible with skia-canvas putImageData
|
|
100
|
+
*
|
|
101
|
+
* @param data - Raw pixel data (RGBA format)
|
|
102
|
+
* @param width - Image width in pixels
|
|
103
|
+
* @param height - Image height in pixels
|
|
104
|
+
* @returns ImageData-like object
|
|
105
|
+
*/
|
|
106
|
+
export function pixelsToImageData(data, width, height) {
|
|
107
|
+
const clampedArray = new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength);
|
|
108
|
+
return {
|
|
109
|
+
data: clampedArray,
|
|
110
|
+
width,
|
|
111
|
+
height,
|
|
112
|
+
colorSpace: 'srgb',
|
|
113
|
+
colorType: 'rgba', // Required by skia-canvas
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Draw raw RGBA pixels to canvas
|
|
118
|
+
*
|
|
119
|
+
* @param canvas - The skia-canvas Canvas instance
|
|
120
|
+
* @param data - Raw pixel data (RGBA format)
|
|
121
|
+
* @param width - Image width in pixels
|
|
122
|
+
* @param height - Image height in pixels
|
|
123
|
+
*/
|
|
124
|
+
export function drawPixelsToCanvas(canvas, data, width, height) {
|
|
125
|
+
const ctx = canvas.getContext('2d');
|
|
126
|
+
const imageData = pixelsToImageData(data, width, height);
|
|
127
|
+
ctx.putImageData(imageData, 0, 0);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create a Uint8Array view of a Buffer without copying
|
|
131
|
+
*
|
|
132
|
+
* @param buffer - Node.js Buffer
|
|
133
|
+
* @returns Uint8Array view of the same memory
|
|
134
|
+
*/
|
|
135
|
+
export function bufferToUint8Array(buffer) {
|
|
136
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Resize raw pixel data using canvas
|
|
140
|
+
*
|
|
141
|
+
* @param data - Source pixel data (RGBA format)
|
|
142
|
+
* @param srcWidth - Source width
|
|
143
|
+
* @param srcHeight - Source height
|
|
144
|
+
* @param dstWidth - Destination width
|
|
145
|
+
* @param dstHeight - Destination height
|
|
146
|
+
* @param canvas - Optional canvas to reuse (for performance)
|
|
147
|
+
* @returns Resized pixel data as Buffer
|
|
148
|
+
*/
|
|
149
|
+
export function resizePixels(data, srcWidth, srcHeight, dstWidth, dstHeight, canvas) {
|
|
150
|
+
// Create source canvas
|
|
151
|
+
const srcCanvas = new Canvas(srcWidth, srcHeight);
|
|
152
|
+
const srcCtx = srcCanvas.getContext('2d');
|
|
153
|
+
const srcImageData = pixelsToImageData(data, srcWidth, srcHeight);
|
|
154
|
+
srcCtx.putImageData(srcImageData, 0, 0);
|
|
155
|
+
// Create or reuse destination canvas
|
|
156
|
+
const dstCanvas = canvas ?? new Canvas(dstWidth, dstHeight);
|
|
157
|
+
if (dstCanvas.width !== dstWidth || dstCanvas.height !== dstHeight) {
|
|
158
|
+
// Canvas dimensions don't match, need to create new one
|
|
159
|
+
const newDst = new Canvas(dstWidth, dstHeight);
|
|
160
|
+
const dstCtx = newDst.getContext('2d');
|
|
161
|
+
dstCtx.drawImage(srcCanvas, 0, 0, dstWidth, dstHeight);
|
|
162
|
+
return getRawPixels(newDst);
|
|
163
|
+
}
|
|
164
|
+
const dstCtx = dstCanvas.getContext('2d');
|
|
165
|
+
resetCanvas(dstCtx);
|
|
166
|
+
dstCtx.drawImage(srcCanvas, 0, 0, dstWidth, dstHeight);
|
|
167
|
+
return getRawPixels(dstCanvas);
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=canvas-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-utils.js","sourceRoot":"","sources":["../../src/canvas/canvas-utils.ts"],"names":[],"mappings":"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"}
|