UNPKG

@julesl23/s5js

Version:

Enhanced TypeScript SDK for S5 decentralized storage with path-based API, media processing, and directory utilities

212 lines 7.04 kB
import { ThumbnailGenerator } from '../thumbnail/generator.js'; /** * Abstract base class for progressive images */ class ProgressiveImage { layers; constructor(layers) { this.layers = layers; } /** * Get all layers */ getAllLayers() { return this.layers; } } /** * Progressive JPEG implementation with multiple scans */ class ProgressiveJPEG extends ProgressiveImage { getLayer(index) { return this.layers[index]; } get layerCount() { return this.layers.length; } toBlob() { // For progressive JPEG, we combine all layers for the final image // In a real implementation, this would be a properly encoded progressive JPEG // For now, we return the highest quality layer const bestLayer = this.layers[this.layers.length - 1]; return new Blob([new Uint8Array(bestLayer.data)], { type: 'image/jpeg' }); } } /** * Progressive PNG implementation with Adam7 interlacing */ class ProgressivePNG extends ProgressiveImage { getLayer(index) { // PNG interlacing is handled internally as a single file return index === 0 ? this.layers[0] : undefined; } get layerCount() { return 1; // PNG progressive is a single interlaced file } toBlob() { return new Blob([new Uint8Array(this.layers[0].data)], { type: 'image/png' }); } } /** * Progressive WebP implementation with multiple quality levels */ class ProgressiveWebP extends ProgressiveImage { getLayer(index) { return this.layers[index]; } get layerCount() { return this.layers.length; } toBlob() { // Return highest quality version const bestLayer = this.layers[this.layers.length - 1]; return new Blob([new Uint8Array(bestLayer.data)], { type: 'image/webp' }); } } /** * ProgressiveImageLoader creates progressive/interlaced images * for efficient loading in web applications */ export class ProgressiveImageLoader { /** * Create a progressive image from a blob */ static async createProgressive(blob, options = {}) { // Validate blob if (blob.size === 0) { throw new Error('Empty blob'); } // Detect format const format = await this.detectFormat(blob); // Route to appropriate handler based on format switch (format) { case 'jpeg': return this.createProgressiveJPEG(blob, options); case 'png': return this.createProgressivePNG(blob, options); case 'webp': return this.createProgressiveWebP(blob, options); default: throw new Error(`Unsupported format for progressive loading: ${format}`); } } /** * Create progressive JPEG with multiple quality scans */ static async createProgressiveJPEG(blob, options) { const scans = options.progressiveScans ?? 3; const qualityLevels = options.qualityLevels ?? [20, 50, 85]; const layers = []; // Generate thumbnails at different quality levels to simulate progressive scans for (let i = 0; i < scans; i++) { const quality = qualityLevels[i] ?? 85; // Use default if not specified const isBaseline = i === 0; // Use ThumbnailGenerator to create different quality versions // Use very large dimensions to preserve original size const result = await ThumbnailGenerator.generateThumbnail(blob, { quality, format: 'jpeg', maxWidth: 10000, maxHeight: 10000, }); const arrayBuffer = await result.blob.arrayBuffer(); const data = new Uint8Array(arrayBuffer); layers.push({ data, quality, isBaseline, scanNumber: i, }); } return new ProgressiveJPEG(layers); } /** * Create progressive PNG with Adam7 interlacing */ static async createProgressivePNG(blob, options) { const interlace = options.interlace ?? true; if (!interlace) { // Return non-interlaced PNG as single layer const arrayBuffer = await blob.arrayBuffer(); const data = new Uint8Array(arrayBuffer); return new ProgressivePNG([ { data, quality: 100, isBaseline: true, scanNumber: 0, }, ]); } // Create interlaced PNG // In a real implementation, this would use a PNG encoder with Adam7 interlacing // For now, we use the original blob data const arrayBuffer = await blob.arrayBuffer(); const data = new Uint8Array(arrayBuffer); return new ProgressivePNG([ { data, quality: 100, isBaseline: true, scanNumber: 0, }, ]); } /** * Create progressive WebP with multiple quality levels */ static async createProgressiveWebP(blob, options) { const qualityLevels = options.qualityLevels ?? [30, 60, 90]; const layers = []; // Generate WebP versions at different quality levels for (let i = 0; i < qualityLevels.length; i++) { const quality = qualityLevels[i]; const result = await ThumbnailGenerator.generateThumbnail(blob, { quality, format: 'webp', maxWidth: 10000, maxHeight: 10000, }); const arrayBuffer = await result.blob.arrayBuffer(); const data = new Uint8Array(arrayBuffer); layers.push({ data, quality, isBaseline: i === 0, scanNumber: i, }); } return new ProgressiveWebP(layers); } /** * Detect image format from blob data */ static async detectFormat(blob) { const arrayBuffer = await blob.arrayBuffer(); const header = new Uint8Array(arrayBuffer).slice(0, 16); // JPEG: FF D8 FF if (header[0] === 0xff && header[1] === 0xd8 && header[2] === 0xff) { return 'jpeg'; } // PNG: 89 50 4E 47 0D 0A 1A 0A if (header[0] === 0x89 && header[1] === 0x50 && header[2] === 0x4e && header[3] === 0x47) { return 'png'; } // WebP: RIFF....WEBP if (header[0] === 0x52 && header[1] === 0x49 && header[2] === 0x46 && header[3] === 0x46 && header[8] === 0x57 && header[9] === 0x45 && header[10] === 0x42 && header[11] === 0x50) { return 'webp'; } return 'unknown'; } } //# sourceMappingURL=loader.js.map