feat(core): add quality parameter for image compression control
This commit is contained in:
14
changelog.md
14
changelog.md
@@ -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
|
||||||
|
@@ -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",
|
||||||
|
46
readme.md
46
readme.md
@@ -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)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user