Skip to content

Commit f505c33

Browse files
committed
[TASK] Ship sensible defaults for AVIF and JPEG XL, document per-backend params
1 parent 661baa6 commit f505c33

3 files changed

Lines changed: 133 additions & 19 deletions

File tree

Classes/Command/DiagnoseCommand.php

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -663,25 +663,97 @@ private function checkCustomConverter(string $converterClass): array
663663

664664
private function checkParameterParsing(SymfonyStyle $io, OutputFormat $format): void
665665
{
666+
// Distinguish three states so the message tells the admin exactly what to do:
667+
// (1) parameters string is completely empty → no per-mime check possible, one warning
668+
// (2) parameters set but no '::' separator → applies to every mime type, one warning
669+
// (3) parameters set with mime entries but the → per-mime warning for each missing entry
670+
// current mime isn't covered
671+
$suggestion = $this->recommendedParametersFor($format);
672+
673+
if ('' === $this->configuration->getRawParameters($format)) {
674+
$this->writeStatus($io, '!', \sprintf('parameters_%s is empty — recommended: %s', $format->value, $suggestion));
675+
++$this->warningCount;
676+
$this->captureFirstFailure(\sprintf(
677+
"parameters_%s is empty. Set it in the Extension Configuration to:\n %s",
678+
$format->value,
679+
$suggestion,
680+
));
681+
682+
return;
683+
}
684+
685+
$hasMimePrefix = \str_contains($this->configuration->getRawParameters($format), '::');
686+
if (!$hasMimePrefix) {
687+
$this->writeStatus($io, '!', \sprintf('parameters_%s has no `mime/type::` prefix — the same options apply to every supported mime type. Recommended: %s', $format->value, $suggestion));
688+
++$this->warningCount;
689+
$this->captureFirstFailure(\sprintf(
690+
"parameters_%s uses the legacy single-options format (no `mime/type::` prefix). It passes the same options to every supported mime. Replace with:\n %s",
691+
$format->value,
692+
$suggestion,
693+
));
694+
695+
return;
696+
}
697+
666698
foreach (\Plan2net\Webp\Format\SourceMimeType::all() as $mimeType) {
667699
if (!$this->configuration->isSupportedMimeTypeFor($format, $mimeType)) {
668700
continue;
669701
}
670-
$resolved = $this->configuration->getParametersFor($format, $mimeType);
671-
if (null === $resolved) {
672-
$this->writeStatus($io, '!', \sprintf('parameters for %s @ %s could not be resolved — falls back to old single-options format', $format->value, $mimeType));
702+
if (null === $this->configuration->getParametersFor($format, $mimeType)) {
703+
$mimeSuggestion = $this->recommendedMimeEntry($format, $mimeType);
704+
$this->writeStatus($io, '!', \sprintf('parameters_%s has no entry for %s — append `|%s`', $format->value, $mimeType, $mimeSuggestion));
673705
++$this->warningCount;
674706
$this->captureFirstFailure(\sprintf(
675-
"Converter parameters cannot be resolved for format %s and mime type %s.\nThe parameters_%s string should look like 'image/jpeg::Q=85|image/png::Q=75'. Check the parameters_%s setting in the extension config.",
707+
'parameters_%s is missing a `%s::…` entry. Append `|%s` to the existing value so files of that mime type are converted.',
676708
$format->value,
677709
$mimeType,
678-
$format->value,
679-
$format->value,
710+
$mimeSuggestion,
680711
));
681712
}
682713
}
683714
}
684715

716+
/**
717+
* Looks at the currently-configured converter and returns the recommended
718+
* parameter string for the given output format. Defaults to the
719+
* ImageMagick recipe (which is what ext_conf_template ships) so the
720+
* suggestion is always concrete instead of "see README".
721+
*/
722+
private function recommendedParametersFor(OutputFormat $format): string
723+
{
724+
$converterClass = $this->configuration->getConverterFor($format);
725+
726+
if (VipsConverter::class === $converterClass) {
727+
return match ($format) {
728+
OutputFormat::Webp => 'image/jpeg::Q=85 smart_subsample=true effort=4|image/png::Q=75 lossless=true effort=4|image/gif::Q=75 lossless=true mixed=true effort=4',
729+
OutputFormat::Avif => 'image/jpeg::Q=60 effort=4|image/png::Q=60 effort=4|image/gif::Q=60 effort=4',
730+
OutputFormat::Jxl => 'image/jpeg::Q=75 effort=7|image/png::lossless=true effort=7|image/gif::lossless=true effort=7',
731+
};
732+
}
733+
734+
// MagickConverter, ExternalConverter and any custom converter that
735+
// accepts ImageMagick-style flags fall through to this branch — also
736+
// matches the empty `converter_<format>` default during boot.
737+
return match ($format) {
738+
OutputFormat::Webp => 'image/jpeg::-quality 85 -define webp:lossless=false|image/png::-quality 75 -define webp:lossless=true|image/gif::-quality 85 -define webp:lossless=true',
739+
OutputFormat::Avif => 'image/jpeg::-quality 60|image/png::-quality 75|image/gif::-quality 60',
740+
OutputFormat::Jxl => 'image/jpeg::-quality 75|image/png::-quality 90|image/gif::-quality 75',
741+
};
742+
}
743+
744+
private function recommendedMimeEntry(OutputFormat $format, string $mimeType): string
745+
{
746+
foreach (\explode('|', $this->recommendedParametersFor($format)) as $segment) {
747+
if (\str_starts_with($segment, $mimeType . '::')) {
748+
return $segment;
749+
}
750+
}
751+
752+
// The recommendation table above always covers jpeg/png/gif, so this
753+
// is only reached for hypothetical future mime types.
754+
return $mimeType . '::-quality 75';
755+
}
756+
685757
/**
686758
* @param list<OutputFormat> $selectedFormats
687759
*/

README.md

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,12 @@ Existing installs keep generating WebP only because `formats_enabled` defaults t
162162
| [`hide_webp`](#hide_webp) | `1` | Hide generated sibling files (`.webp` / `.avif` / `.jxl`) in the BE file list |
163163
| [`mime_types`](#mime_types) | `image/jpeg,image/png,image/gif` | Source mime types convertible to `webp` |
164164
| [`parameters`](#parameters) | See below | Per-mime-type WebP converter parameters (the `webp` slot) |
165+
| [`parameters_avif`](#parameters_avif) | See below | Per-mime-type AVIF converter parameters |
166+
| [`parameters_jxl`](#parameters_jxl) | See below | Per-mime-type JPEG XL converter parameters |
165167
| [`silent`](#silent) | `1` | Suppress converter stdout/stderr (Linux only) |
166168
| [`use_system_settings`](#use_system_settings) | `1` | Reuse GFX color profile settings (MagickConverter) |
167169

168-
Each non-webp format adds its own `converter_<format>`, `parameters_<format>`, and `mime_types_<format>` settings in dedicated `cat=avif` / `cat=jxl` tabs of the Extension Configuration form. See [`formats_enabled`](#formats_enabled) below.
170+
Each non-webp format adds its own `converter_<format>`, `parameters_<format>`, and `mime_types_<format>` settings in dedicated `cat=avif` / `cat=jxl` tabs of the Extension Configuration form. See [`formats_enabled`](#formats_enabled) and the per-format `parameters_avif` / `parameters_jxl` sections below.
169171

170172
### `async`
171173

@@ -231,17 +233,12 @@ Comma-separated list of output formats this install should produce. Each enabled
231233

232234
Each non-webp format reads its converter and parameters from per-format keys (in their own `cat=avif` / `cat=jxl` tabs in the Extension Configuration form):
233235

234-
- `converter_avif`, `parameters_avif`, `mime_types_avif`
235-
- `converter_jxl`, `parameters_jxl`, `mime_types_jxl`
236+
- `converter_avif`, [`parameters_avif`](#parameters_avif), `mime_types_avif`
237+
- `converter_jxl`, [`parameters_jxl`](#parameters_jxl), `mime_types_jxl`
236238

237239
The legacy `converter` + `parameters` + `mime_types` keys remain the source of truth for the WebP slot.
238240

239-
**Recommended parameter strings** for libvips:
240-
241-
```
242-
parameters_avif = image/jpeg::Q=60 effort=4|image/png::Q=60 effort=4|image/gif::Q=60 effort=4
243-
parameters_jxl = image/jpeg::Q=75 effort=7|image/png::lossless=true effort=7|image/gif::lossless=true effort=7
244-
```
241+
Both `parameters_avif` and `parameters_jxl` ship with ImageMagick-compatible defaults so enabling a format works out of the box on the typical TYPO3 host. Tune to your stack — recipes for libvips and external binaries are under [`parameters_avif`](#parameters_avif) and [`parameters_jxl`](#parameters_jxl) below.
245242

246243
### `hide_webp`
247244

@@ -333,6 +330,51 @@ Options are passed straight to libvips's `webpsave` — see the [option referenc
333330
> [!IMPORTANT]
334331
> Set `ffi.enable=true` in php.ini (not `preload` — jcupitt/vips does not support FFI preloading). On PHP 8.3+ also set `zend.max_allowed_stack_size=-1`; without it the default stack limit can cause spurious conversion failures.
335332
333+
### `parameters_avif`
334+
335+
Same `mime/type::params|…` syntax as [`parameters`](#parameters). Default targets ImageMagick (matches the default `converter_avif`):
336+
337+
```
338+
parameters_avif = image/jpeg::-quality 60|image/png::-quality 75|image/gif::-quality 60
339+
```
340+
341+
| Backend | Recommended `parameters_avif` |
342+
|---------------------|----------------------------------------------------------------------------------------|
343+
| `MagickConverter` | `image/jpeg::-quality 60\|image/png::-quality 75\|image/gif::-quality 60` |
344+
| `VipsConverter` | `image/jpeg::Q=60 effort=4\|image/png::Q=60 effort=4\|image/gif::Q=60 effort=4` |
345+
| `ExternalConverter` | `image/jpeg::/usr/bin/avifenc --min 30 --max 50 %s %s\|image/png::…\|image/gif::…` |
346+
347+
AVIF at quality ~60 typically matches WebP at quality ~85 in filesize, often at better SSIM. For ImageMagick, `-quality` covers the common case; advanced encoders also accept `-define heic:speed=2` (slower, better compression) on builds with the libheif AV1 delegate.
348+
349+
References:
350+
351+
- [libheif AV1 encoder options](https://github.com/strukturag/libheif/blob/master/aom-options.md) (used by ImageMagick when compiled with libheif)
352+
- [libvips `heifsave` reference](https://www.libvips.org/API/current/VipsForeignSave.html#vips-heifsave) (the libvips AVIF entry point)
353+
- [`avifenc`](https://github.com/AOMediaCodec/libavif#installation) command-line flags
354+
355+
`webp:diagnose` reports per-mime-type parameter resolution; if you enable AVIF and leave entries missing, it tells you which lines to add.
356+
357+
### `parameters_jxl`
358+
359+
Same syntax. Default targets ImageMagick:
360+
361+
```
362+
parameters_jxl = image/jpeg::-quality 75|image/png::-quality 90|image/gif::-quality 75
363+
```
364+
365+
| Backend | Recommended `parameters_jxl` |
366+
|---------------------|----------------------------------------------------------------------------------------------------|
367+
| `MagickConverter` | `image/jpeg::-quality 75\|image/png::-quality 90\|image/gif::-quality 75` |
368+
| `VipsConverter` | `image/jpeg::Q=75 effort=7\|image/png::lossless=true effort=7\|image/gif::lossless=true effort=7` |
369+
| `ExternalConverter` | `image/jpeg::/usr/bin/cjxl --quality 75 %s %s\|image/png::/usr/bin/cjxl --lossless_jpeg=0 %s %s\|…`|
370+
371+
JXL preserves PNG well in modular (lossless) mode — for `image/png`, prefer a higher quality value (or `lossless=true` on libvips) than for JPEG.
372+
373+
References:
374+
375+
- [libjxl encoder options](https://github.com/libjxl/libjxl/blob/main/doc/man/cjxl.txt)
376+
- [libvips `jxlsave` reference](https://www.libvips.org/API/current/VipsForeignSave.html#vips-jxlsave)
377+
336378
### `silent`
337379

338380
```

ext_conf_template.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ async = 0
2323
# cat=async; type=int+; label=Random sleep (ms) between conversions in the worker (0 = no sleep); each pause is randomized between half and 1.5x of this value
2424
async_throttle_ms = 0
2525
# cat=avif; type=options[Local ImageMagick/GraphicsMagick converter=Plan2net\Webp\Converter\MagickConverter,External converter=Plan2net\Webp\Converter\ExternalConverter,libvips (native)=Plan2net\Webp\Converter\VipsConverter]; label=AVIF conversion adapter (see documentation)
26-
converter_avif =
26+
converter_avif = Plan2net\Webp\Converter\MagickConverter
2727
# cat=avif; type=string; label=AVIF conversion parameters (per mime type, separated by |)
28-
parameters_avif =
28+
parameters_avif = image/jpeg::-quality 60|image/png::-quality 75|image/gif::-quality 60
2929
# cat=avif; type=string; label=AVIF supported mime types (comma separated)
3030
mime_types_avif = image/jpeg,image/png,image/gif
3131
# cat=jxl; type=options[Local ImageMagick/GraphicsMagick converter=Plan2net\Webp\Converter\MagickConverter,External converter=Plan2net\Webp\Converter\ExternalConverter,libvips (native)=Plan2net\Webp\Converter\VipsConverter]; label=JPEG XL conversion adapter (see documentation)
32-
converter_jxl =
32+
converter_jxl = Plan2net\Webp\Converter\MagickConverter
3333
# cat=jxl; type=string; label=JPEG XL conversion parameters (per mime type, separated by |)
34-
parameters_jxl =
34+
parameters_jxl = image/jpeg::-quality 75|image/png::-quality 90|image/gif::-quality 75
3535
# cat=jxl; type=string; label=JPEG XL supported mime types (comma separated)
3636
mime_types_jxl = image/jpeg,image/png,image/gif

0 commit comments

Comments
 (0)