Skip to content

Commit

Permalink
Merge pull request #198 from 0xb4lint/avif-support
Browse files Browse the repository at this point in the history
added AVIF support
  • Loading branch information
freekmurze committed Jul 22, 2023
2 parents 40a7206 + d038171 commit 9551e2d
Show file tree
Hide file tree
Showing 17 changed files with 147 additions and 29 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Install optimizers
run: |
sudo apt-get update -y
sudo apt-get install -y jpegoptim pngquant gifsicle optipng libjpeg-progs webp
sudo apt-get install -y jpegoptim pngquant gifsicle optipng libjpeg-progs webp libavif-bin
sudo npm install -g svgo
- name: Setup PHP
Expand Down
90 changes: 62 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
![Tests](https://github.com/spatie/image-optimizer/workflows/Tests/badge.svg)
[![Total Downloads](https://img.shields.io/packagist/dt/spatie/image-optimizer.svg?style=flat-square)](https://packagist.org/packages/spatie/image-optimizer)

This package can optimize PNGs, JPGs, WEBPs, SVGs and GIFs by running them through a chain of various [image optimization tools](#optimization-tools). Here's how you can use it:
This package can optimize PNGs, JPGs, WEBPs, AVIFs, SVGs and GIFs by running them through a chain of various [image optimization tools](#optimization-tools). Here's how you can use it:

```php
use Spatie\ImageOptimizer\OptimizerChainFactory;
Expand Down Expand Up @@ -50,6 +50,7 @@ The package will use these optimizers if they are present on your system:
- [SVGO 1](https://github.com/svg/svgo)
- [Gifsicle](http://www.lcdf.org/gifsicle/)
- [cwebp](https://developers.google.com/speed/webp/docs/precompiled)
- [avifenc](https://github.com/AOMediaCodec/libavif/blob/main/doc/avifenc.1.md)

Here's how to install all the optimizers on Ubuntu:

Expand All @@ -60,6 +61,7 @@ sudo apt-get install pngquant
sudo npm install -g svgo
sudo apt-get install gifsicle
sudo apt-get install webp
sudo apt-get install libavif-bin
```

And here's how to install the binaries on MacOS (using [Homebrew](https://brew.sh/)):
Expand All @@ -71,6 +73,7 @@ brew install pngquant
npm install -g svgo
brew install gifsicle
brew install webp
brew install libavif
```
And here's how to install the binaries on Fedora/RHEL/CentOS:

Expand All @@ -82,6 +85,7 @@ sudo dnf install pngquant
sudo npm install -g svgo
sudo dnf install gifsicle
sudo dnf install libwebp-tools
sudo dnf install libavif-tools
```

## Which tools will do what?
Expand Down Expand Up @@ -122,6 +126,20 @@ WEBPs will be optimized by [Cwebp](https://developers.google.com/speed/webp/docs

(Settings are original taken from [here](https://medium.com/@vinhlh/how-i-apply-webp-for-optimizing-images-9b11068db349))

### AVIFs

AVIFs will be optimized by [avifenc](https://github.com/AOMediaCodec/libavif/blob/main/doc/avifenc.1.md). These options will be used:
- `-a cq-level=23`: Constant Quality level. Lower values mean better quality and greater file size (0-63).
- `-j all`: Number of jobs (worker threads, `all` uses all available cores).
- `--min 0`: Min quantizer for color (0-63).
- `--max 63`: Max quantizer for color (0-63).
- `--minalpha 0`: Min quantizer for alpha (0-63).
- `--maxalpha 63`: Max quantizer for alpha (0-63).
- `-a end-usage=q` Rate control mode set to Constant Quality mode.
- `-a tune=ssim`: SSIM as tune the encoder for distortion metric.

(Settings are original taken from [here](https://web.dev/compress-images-avif/#create-an-avif-image-with-default-settings) and [here](https://github.com/feat-agency/avif))

## Usage

This is the default way to use the package:
Expand Down Expand Up @@ -273,57 +291,73 @@ A logger is a class that implements `Psr\Log\LoggerInterface`. A good logging li

Here are some real life example conversions done by this package.

### png
Methodology for JPG, WEBP, AVIF images: the [original image](https://unsplash.com/photos/jTeQavJjBDs) has been fed to [spatie/image](https://github.com/spatie/image) (using the default GD driver) and resized to 2048px width:

Original: Photoshop 'Save for web' | PNG-24 with transparency<br>
39 KB
```php
Spatie\Image\Image::load('original.jpg')
->width(2048)
->save('image.jpg'); // image.png, image.webp, image.avif
```

![Original](https://spatie.github.io/image-optimizer/examples/logo.png)
### jpg

Optimized<br>
16 KB (-59%, DSSIM: 0.00000251)
![Original](https://spatie.github.io/image-optimizer/examples/image.jpg)
Original<br>
771 KB

![Optimized](https://spatie.github.io/image-optimizer/examples/logo-optimized.png)
![Optimized](https://spatie.github.io/image-optimizer/examples/image-optimized.jpg)
Optimized<br>
511 KB (-33.7%, DSSIM: 0.00052061)

### jpg
credits: Jeff Sheldon, via [Unsplash](https://unsplash.com)

Original: Photoshop 'Save for web' | quality 60, optimized<br>
534 KB
### webp

![Original](https://spatie.github.io/image-optimizer/examples/image.jpg)
![Original](https://spatie.github.io/image-optimizer/examples/image.webp)
Original<br>
461 KB

![Optimized](https://spatie.github.io/image-optimizer/examples/image-optimized.webp)
Optimized<br>
514 KB (-3.9%, DSSIM: 0.00000000)
184 KB (-60.0%, DSSIM: 0.00166036)

![Optimized](https://spatie.github.io/image-optimizer/examples/image-optimized.jpg)
credits: Jeff Sheldon, via [Unsplash](https://unsplash.com)

### avif

![Original](https://spatie.github.io/image-optimizer/examples/image.avif)
Original<br>
725 KB

![Optimized](https://spatie.github.io/image-optimizer/examples/image-optimized.avif)
Optimized<br>
194 KB (-73.2%, DSSIM: 0.00163751)

credits: Jeff Sheldon, via [Unsplash](https://unsplash.com)

### svg
### png

Original: Illustrator | Web optimized SVG export<br>
25 KB
Original: Photoshop 'Save for web' | PNG-24 with transparency<br>
39 KB

![Original](https://spatie.github.io/image-optimizer/examples/graph.svg)
![Original](https://spatie.github.io/image-optimizer/examples/logo.png)

Optimized<br>
20 KB (-21.5%)
16 KB (-59%, DSSIM: 0.00000251)

![Optimized](https://spatie.github.io/image-optimizer/examples/graph-optimized.svg)
![Optimized](https://spatie.github.io/image-optimizer/examples/logo-optimized.png)

### webp
### svg

Original: WebPonize<br>
502 KB
Original: Illustrator | Web optimized SVG export<br>
25 KB

![Original](https://spatie.github.io/image-optimizer/examples/image.webp)
![Original](https://spatie.github.io/image-optimizer/examples/graph.svg)

Optimized<br>
413 KB (-17.7%, DSSIM: 0.00040705)

![Optimized](https://spatie.github.io/image-optimizer/examples/image-optimized.webp)
20 KB (-21.5%)

credits: Jeff Sheldon, via [Unsplash](https://unsplash.com)
![Optimized](https://spatie.github.io/image-optimizer/examples/graph-optimized.svg)

## Changelog

Expand Down
Binary file added docs/examples/image-optimized.avif
Binary file not shown.
Binary file modified docs/examples/image-optimized.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/examples/image-optimized.webp
Binary file not shown.
Binary file added docs/examples/image.avif
Binary file not shown.
Binary file modified docs/examples/image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/examples/image.webp
Binary file not shown.
7 changes: 7 additions & 0 deletions src/Optimizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,11 @@ public function setOptions(array $options = []);
* @return string
*/
public function getCommand(): string;

/**
* Get the temporary file's path.
*
* @return null|string
*/
public function getTmpPath(): ?string;
}
7 changes: 7 additions & 0 deletions src/OptimizerChain.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ protected function applyOptimizer(Optimizer $optimizer, Image $image)
->setTimeout($this->timeout)
->run();

if (
($tmpPath = $optimizer->getTmpPath()) &&
file_exists($tmpPath)
) {
unlink($tmpPath);
}

$this->logResult($process);
}

Expand Down
13 changes: 13 additions & 0 deletions src/OptimizerChainFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Spatie\ImageOptimizer;

use Spatie\ImageOptimizer\Optimizers\Avifenc;
use Spatie\ImageOptimizer\Optimizers\Cwebp;
use Spatie\ImageOptimizer\Optimizers\Gifsicle;
use Spatie\ImageOptimizer\Optimizers\Jpegoptim;
Expand All @@ -16,10 +17,12 @@ public static function create(array $config = []): OptimizerChain
$jpegQuality = '--max=85';
$pngQuality = '--quality=85';
$webpQuality = '-q 80';
$avifQuality = '-a cq-level=23';
if (isset($config['quality'])) {
$jpegQuality = '--max='.$config['quality'];
$pngQuality = '--quality='.$config['quality'];
$webpQuality = '-q '.$config['quality'];
$avifQuality = '-a cq-level='.round(63 - $config['quality'] * 0.63);
}

return (new OptimizerChain())
Expand Down Expand Up @@ -54,6 +57,16 @@ public static function create(array $config = []): OptimizerChain
'-m 6',
'-pass 10',
'-mt',
]))
->addOptimizer(new Avifenc([
$avifQuality,
'-j all',
'--min 0',
'--max 63',
'--minalpha 0',
'--maxalpha 63',
'-a end-usage=q',
'-a tune=ssim',
]));
}
}
39 changes: 39 additions & 0 deletions src/Optimizers/Avifenc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Spatie\ImageOptimizer\Optimizers;

use Spatie\ImageOptimizer\Image;

class Avifenc extends BaseOptimizer
{
public $binaryName = 'avifenc';
public $decodeBinaryName = 'avifdec';

public function canHandle(Image $image): bool
{
return $image->mime() === 'image/avif' || $image->extension() === 'avif';
}

public function getCommand(): string
{
$this->tmpPath = tempnam(sys_get_temp_dir(), 'avifdec') . '.png';

$decodeOptionString = implode(' ', [
'-j all',
'--ignore-icc',
'--no-strict',
'--png-compress 0',
]);
$encodeOptionString = implode(' ', $this->options);

$decode = "\"{$this->binaryPath}{$this->decodeBinaryName}\" {$decodeOptionString}"
.' '.escapeshellarg($this->imagePath)
.' '.escapeshellarg($this->tmpPath);

$encode = "\"{$this->binaryPath}{$this->binaryName}\" {$encodeOptionString}"
.' '.escapeshellarg($this->tmpPath)
.' '.escapeshellarg($this->imagePath);

return $decode . ' && ' . $encode;
}
}
7 changes: 7 additions & 0 deletions src/Optimizers/BaseOptimizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ abstract class BaseOptimizer implements Optimizer

public $binaryPath = '';

public $tmpPath = null;

public function __construct($options = [])
{
$this->setOptions($options);
Expand Down Expand Up @@ -53,4 +55,9 @@ public function getCommand(): string

return "\"{$this->binaryPath}{$this->binaryName}\" {$optionString} ".escapeshellarg($this->imagePath);
}

public function getTmpPath(): ?string
{
return $this->tmpPath;
}
}
11 changes: 11 additions & 0 deletions tests/OptimizerChainFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use function PHPUnit\Framework\assertFileEquals;

use Spatie\ImageOptimizer\OptimizerChainFactory;
use Spatie\ImageOptimizer\Optimizers\Avifenc;
use Spatie\ImageOptimizer\Optimizers\Cwebp;
use Spatie\ImageOptimizer\Optimizers\Gifsicle;
use Spatie\ImageOptimizer\Optimizers\Jpegoptim;
Expand Down Expand Up @@ -69,6 +70,16 @@
$this->assertOptimizersUsed(Cwebp::class);
});

it('can optimize an avif', function () {
$tempFilePath = getTempFilePath('image.avif');

$this->optimizerChain->optimize($tempFilePath);

$this->assertDecreasedFileSize($tempFilePath, getTestFilePath('image.avif'));

$this->assertOptimizersUsed(Avifenc::class);
});

it('will not not touch a non image file', function () {
$tempFilePath = getTempFilePath('test.txt');

Expand Down
Binary file added tests/testfiles/image.avif
Binary file not shown.
Binary file modified tests/testfiles/image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/testfiles/image.webp
Binary file not shown.

0 comments on commit 9551e2d

Please sign in to comment.