feat(core): add quality parameter for image compression control

This commit is contained in:
2025-08-02 17:17:52 +00:00
parent 764b074ca4
commit 2eb887dce7
4 changed files with 94 additions and 19 deletions

View File

@@ -1,5 +1,19 @@
# Changelog # Changelog
## [1.2.0] - 2025-01-02
### Added
- Quality parameter support for image compression
- New `quality` option (1-100) in IAssetVariation interface for controlling compression quality
- Quality control implementation for both Sharp and Jimp modes
- Support for quality settings on all lossy formats (JPEG, WebP, AVIF)
- Enhanced documentation with quality control examples
### Changed
- Jimp mode now uses fall-through pattern in format switch for cleaner code
- Console logging when Jimp falls back to JPEG for unsupported formats (WebP, AVIF)
- Updated examples in documentation to showcase quality parameter usage
## [1.1.0] - 2025-01-02 ## [1.1.0] - 2025-01-02
### Added ### Added

View File

@@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/smartjimp", "name": "@push.rocks/smartjimp",
"version": "1.1.0", "version": "1.2.0",
"private": false, "private": false,
"description": "A TypeScript library for image processing combining both sharp and jimp libraries.", "description": "A TypeScript library for image processing combining both sharp and jimp libraries.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",

View File

@@ -93,6 +93,38 @@ const avifBuffer = await processor.getFromSmartfile(imageFile, {
}); });
``` ```
### 🎚️ Quality Control
Fine-tune image compression quality for the perfect balance between file size and visual fidelity:
```typescript
// High quality JPEG (larger file size)
const highQualityJpeg = await processor.getFromSmartfile(imageFile, {
format: 'jpeg',
quality: 90 // 1-100, higher = better quality
});
// Optimized for web (smaller file size)
const webOptimized = await processor.getFromSmartfile(imageFile, {
format: 'jpeg',
quality: 75, // Good balance for web
progressive: true
});
// Ultra-compressed WebP
const tinyWebP = await processor.getFromSmartfile(imageFile, {
format: 'webp',
quality: 60 // WebP handles lower quality better than JPEG
});
// Quality works with all lossy formats
const qualityAvif = await processor.getFromSmartfile(imageFile, {
format: 'avif',
quality: 80 // AVIF provides excellent quality even at lower values
});
```
> 💡 **Pro tip**: Different formats handle quality settings differently. AVIF and WebP generally look better than JPEG at the same quality level.
### 📸 Progressive JPEG Support ### 📸 Progressive JPEG Support
Create progressive JPEGs that load in multiple passes for better perceived performance: Create progressive JPEGs that load in multiple passes for better perceived performance:
@@ -138,13 +170,15 @@ Work directly with buffers for maximum flexibility:
const processedBuffer = await processor.computeAssetVariation(imageBuffer, { const processedBuffer = await processor.computeAssetVariation(imageBuffer, {
width: 300, width: 300,
format: 'webp', format: 'webp',
quality: 85,
invert: true invert: true
}); });
// Create progressive JPEG from buffer // Create progressive JPEG from buffer
const progressiveBuffer = await processor.computeAssetVariation(imageBuffer, { const progressiveBuffer = await processor.computeAssetVariation(imageBuffer, {
format: 'jpeg', format: 'jpeg',
progressive: true progressive: true,
quality: 88
}); });
// Special AVIF creation method (sharp mode only) // Special AVIF creation method (sharp mode only)
@@ -251,20 +285,23 @@ async function optimizeForWeb(sourceImage: SmartFile) {
// Modern browsers: AVIF // Modern browsers: AVIF
avif: await processor.getFromSmartfile(sourceImage, { avif: await processor.getFromSmartfile(sourceImage, {
format: 'avif', format: 'avif',
width: 1200 width: 1200,
quality: 85 // AVIF excels at lower quality settings
}), }),
// Good browser support: WebP // Good browser support: WebP
webp: await processor.getFromSmartfile(sourceImage, { webp: await processor.getFromSmartfile(sourceImage, {
format: 'webp', format: 'webp',
width: 1200 width: 1200,
quality: 82 // WebP sweet spot for quality/size
}), }),
// Universal fallback: Progressive JPEG // Universal fallback: Progressive JPEG
jpeg: await processor.getFromSmartfile(sourceImage, { jpeg: await processor.getFromSmartfile(sourceImage, {
format: 'jpeg', format: 'jpeg',
progressive: true, progressive: true,
width: 1200 width: 1200,
quality: 80 // Standard web quality
}) })
}; };
@@ -289,6 +326,7 @@ interface IAssetVariation {
height?: number; // Target height (maintains aspect ratio if width not set) height?: number; // Target height (maintains aspect ratio if width not set)
invert?: boolean; // Invert colors (jimp only currently) invert?: boolean; // Invert colors (jimp only currently)
progressive?: boolean; // Create progressive JPEG (sharp only, jpeg format only) progressive?: boolean; // Create progressive JPEG (sharp only, jpeg format only)
quality?: number; // Compression quality 1-100 (lossy formats only)
} }
``` ```

View File

@@ -7,6 +7,7 @@ export interface IAssetVariation {
height?: number; height?: number;
invert?: boolean; invert?: boolean;
progressive?: boolean; progressive?: boolean;
quality?: number;
} }
export interface ISmartJimpOptions { export interface ISmartJimpOptions {
@@ -72,24 +73,44 @@ export class SmartJimp {
} }
switch (assetVariationArg.format) { switch (assetVariationArg.format) {
case 'avif': case 'avif':
sharpImage = resultResize.avif(); const avifOptions: any = {};
if (assetVariationArg.quality !== undefined) {
avifOptions.quality = assetVariationArg.quality;
}
sharpImage = resultResize.avif(avifOptions);
break; break;
case 'webp': case 'webp':
sharpImage = resultResize.webp(); const webpOptions: any = {};
if (assetVariationArg.quality !== undefined) {
webpOptions.quality = assetVariationArg.quality;
}
sharpImage = resultResize.webp(webpOptions);
break; break;
case 'png': case 'png':
sharpImage = resultResize.png(); const pngOptions: any = {};
if (assetVariationArg.quality !== undefined) {
pngOptions.quality = assetVariationArg.quality;
}
sharpImage = resultResize.png(pngOptions);
break; break;
case 'jpeg': case 'jpeg':
sharpImage = resultResize.jpeg({ const jpegOptions: any = {
progressive: assetVariationArg.progressive || false progressive: assetVariationArg.progressive || false
}); };
if (assetVariationArg.quality !== undefined) {
jpegOptions.quality = assetVariationArg.quality;
}
sharpImage = resultResize.jpeg(jpegOptions);
break; break;
default: default:
// Default to JPEG // Default to JPEG
sharpImage = resultResize.jpeg({ const defaultJpegOptions: any = {
progressive: assetVariationArg.progressive || false progressive: assetVariationArg.progressive || false
}); };
if (assetVariationArg.quality !== undefined) {
defaultJpegOptions.quality = assetVariationArg.quality;
}
sharpImage = resultResize.jpeg(defaultJpegOptions);
} }
return sharpImage.toBuffer(); return sharpImage.toBuffer();
} else if (this.options.mode === 'jimp') { } else if (this.options.mode === 'jimp') {
@@ -106,20 +127,22 @@ export class SmartJimp {
} }
// Note: Jimp does not support progressive JPEG encoding // Note: Jimp does not support progressive JPEG encoding
// Progressive option is ignored in jimp mode // Progressive option is ignored in jimp mode
const jpegOptions: any = {};
if (assetVariationArg.quality !== undefined) {
jpegOptions.quality = assetVariationArg.quality;
}
switch (assetVariationArg.format) { switch (assetVariationArg.format) {
case 'png': case 'png':
return await jimpImage.getBuffer("image/png"); return await jimpImage.getBuffer("image/png");
case 'jpeg':
return await jimpImage.getBuffer("image/jpeg");
case 'webp': case 'webp':
// Jimp doesn't support WebP, fallback to JPEG
return await jimpImage.getBuffer("image/jpeg");
case 'avif': case 'avif':
// Jimp doesn't support AVIF, fallback to JPEG console.log(`Jimp doesn't support ${assetVariationArg.format}, falling back to JPEG`);
return await jimpImage.getBuffer("image/jpeg"); // Fall through to JPEG
case 'jpeg':
default: default:
// Default to JPEG // Default to JPEG
return await jimpImage.getBuffer("image/jpeg"); return await jimpImage.getBuffer("image/jpeg", jpegOptions);
} }
} }
} }