Skip to content

Commit

Permalink
[FEATURE] Add AVIF image format @ <picture>
Browse files Browse the repository at this point in the history
  • Loading branch information
xerc committed Feb 10, 2023
1 parent 14d2d8e commit f06edf5
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 19 deletions.
20 changes: 14 additions & 6 deletions Classes/Domain/Model/PictureConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
class PictureConfiguration
{
protected bool $useRetina = false;
// 2x is default. Use multiple if retina is set in TypoScript settings.
protected array $retinaSettings = [2 => '2x'];
protected array $retinaSettings = [2 => '2x']; // 2x is default. Use multiple if retina is set in TypoScript settings.
protected bool $addAvif = false;
protected bool $addWebp = false;
protected bool $lossless = false;
protected bool $addBreakpoints = false;
Expand All @@ -34,11 +34,14 @@ public function __construct(array $arguments, array $typoScriptSettings, FileInt
$this->arguments = $arguments;
$fileExtension = $arguments['fileExtension'] ?? $image->getExtension();
if ($image->getExtension() !== 'svg') {
$this->addWebp = (bool)($fileExtension === 'webp' ? false : ($arguments['addWebp'] ?? $typoScriptSettings['addWebp'] ?? false));
$this->useRetina = (bool)($arguments['useRetina'] ?? $typoScriptSettings['useRetina'] ?? false);
if (isset($typoScriptSettings['retina.'])) {
$this->retinaSettings = $typoScriptSettings['retina.'];
}

$this->addAvif = (bool)($fileExtension === 'avif' ? false : ($arguments['addAvif'] ?? $typoScriptSettings['addAvif'] ?? false));
$this->addWebp = (bool)($fileExtension === 'webp' ? false : ($arguments['addWebp'] ?? $typoScriptSettings['addWebp'] ?? false));

$this->lossless = (bool)($arguments['lossless'] ?? $typoScriptSettings['lossless'] ?? false);
if (isset($typoScriptSettings['breakpoints.'])) {
$this->addBreakpoints = true;
Expand Down Expand Up @@ -105,7 +108,7 @@ public function getRetinaSettings(): array

public function pictureTagShouldBeAdded(): bool
{
return $this->addWebp || $this->addSources;
return $this->addAvif || $this->addWebp || $this->addSources;
}

protected function breakPointsShouldBeAdded(): bool
Expand All @@ -130,12 +133,17 @@ public function sourcesShouldBeAdded(): bool

public function webpShouldBeAddedBeforeSrcset(): bool
{
return $this->addWebp && !$this->addSources;
return $this->addWebp && !$this->addSources;// TODO : MOD ??
}

public function webpShouldBeAddedAfterSrcset(): bool
{
return $this->addWebp && $this->addSources;
return $this->addWebp && $this->addSources;// TODO : MOD ??
}

public function avifShouldBeAdded(): bool
{
return $this->addAvif;
}

public function webpShouldBeAdded(): bool
Expand Down
31 changes: 31 additions & 0 deletions Classes/ViewHelpers/ImageViewHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ public function initializeArguments(): void
'Specifies if image should be displayed for retina as well.'
);

$this->registerArgument(
'addAvif',
'bool',
'Specifies if a picture element with an additional avif image should be rendered.'
);

$this->registerArgument(
'addWebp',
'bool',
Expand Down Expand Up @@ -141,6 +147,7 @@ public function render(): string
// Add a webp source tag and activate nesting within a picture element only if no sources are set.
if ($this->pictureConfiguration->webpShouldBeAddedBeforeSrcset()) {
$tag = $this->addWebpImage($this->arguments);
// TODO : ADD ??
$output[] = $tag->render();
}

Expand All @@ -151,6 +158,11 @@ public function render(): string
$tag = $this->buildSingleTag('source', $sourceConfiguration);
$sourceOutputs[] = $tag->render();

// Build additional source with type avif if attribute addAvif is set and previously build tag is not type of avif already.
if ($type !== 'image/avif' && $this->pictureConfiguration->avifShouldBeAdded()) {
$tag = $this->addAvifImage($sourceConfiguration);
array_unshift($sourceOutputs, $tag->render());
}
// Build additional source with type webp if attribute addWebp is set and previously build tag is not type of webp already.
$type = $tag->getAttribute('type');
if ($type !== 'image/webp' && $this->pictureConfiguration->webpShouldBeAdded()) {
Expand All @@ -165,6 +177,7 @@ public function render(): string
// add a webp fallback for the default/non-sources image if addWebp is set
if ($this->pictureConfiguration->webpShouldBeAddedAfterSrcset()) {
$tag = $this->addWebpImage($this->arguments);
// TODO : ADD ??
$output[] = $tag->render();
}
}
Expand Down Expand Up @@ -377,6 +390,17 @@ protected function addRetina(array $processingInstructions, TagBuilder $tag): vo
$tag->addAttribute('srcset', $tagValue);
}

/**
* Function to add a avif element nested by a picture element.
*/
protected function addAvifImage(array $configuration): TagBuilder
{
$configuration['fileExtension'] = 'avif';
$tag = $this->buildSingleTag('source', $configuration);
$tag->addAttribute('type', 'image/avif');
return $tag;
}

/**
* Function to add a webp element nested by a picture element.
*/
Expand Down Expand Up @@ -474,6 +498,13 @@ protected function getFrontendController(): ?TypoScriptFrontendController
*/
protected function applyProcessingInstructions(array $processingInstructions): ProcessedFile
{
if (($processingInstructions['fileExtension'] ?? '') === 'avif'
&& $this->image->getExtension() !== 'avif'
) {
$jpegQuality = MathUtility::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['GFX']['jpg_quality'], 10, 100, 85);
$processingInstructions['additionalParameters'] = '-quality ' . $jpegQuality;
}

if (($processingInstructions['fileExtension'] ?? '') === 'webp'
&& $this->image->getExtension() !== 'webp'
) {
Expand Down
15 changes: 9 additions & 6 deletions Configuration/TypoScript/setup.typoscript
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
plugin.tx_picture {
# Default for adding of images as webp.
addWebp = 0

# Default for adding retina images.
useRetina = 0

# Default for using lossless compression for webp.
lossless = 0

# Works only in combination with useRetina = 1.
# The key must be the multiplier of the image size and the value the multiplier string added in the srcset
# attribute's value. E.g. add "3 = 3x" to array:
Expand All @@ -17,6 +11,15 @@ plugin.tx_picture {
2 = 2x
}

# Default for adding of images as avif.
addAvif = 0

# Default for adding of images as webp.
addWebp = 0

# Default for using lossless compression for webp.
lossless = 0

# Breakpoints for media query specified in the sources attribute:
# breakpoints {
# sm = 640
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ and renders a single src element or a picture element depending on the specified

Install the extension using composer: `composer req b13/picture`.

Include the TypoScript within your main template:
Include the TypoScript within your main template:

```
@import 'EXT:picture/Configuration/TypoScript/setup.typoscript'
Expand All @@ -34,8 +34,9 @@ See `EXT:picture/Configuration/TypoScript/setup.typoscript` for possible configu

| TypoScript Configuration option | Description |
|---------------------------------|-------------|
| addWebp | Add webp alternative image files as sources. <br>_default: 0_ |
| useRetina | Add retina (2x) version of all images as sizes variants. <br>_default: 0_ |
| addAvif | Add avif alternative image files as sources. <br>_default: 0_ |
| addWebp | Add webp alternative image files as sources. <br>_default: 0_ |
| lossless | Enable lossless compression for webp images. <br>_default: 0_ |
| retina | Use custom or multiple multipliers for calculating retina image variants. <br>_default: <br>retina.2 = 2x<br>Only works in combination with `useRetina = 1` |
| breakpoints | Use named breakpoints for easier markup of different image sizes for one picture element.<br>_default: empty_. |
Expand All @@ -55,14 +56,19 @@ Our image ViewHelper extends from the Fluid Image ViewHelper, so it has all the
If useRetina is set and not further specified in TypoScript setup, the corresponding `img` tag's or `source` tag’s
attribute `srcset` is extended by a 2x retina version of the image.

### addAvif
Adds rendering of additional images in avif format. If it is specified without a given sources attribute, it renders a
picture tag instead of a single img tag in order to maintain a browser fallback. If it is specified together with
`sources` it adds an additional `source` tag above any `source` tag rendered by a given `sources` element.

### addWebp
Adds rendering of additional images in webp format. If it is specified without a given sources attribute, it renders a
picture tag instead of a single img tag in order to maintain a browser fallback. If it is specified together with
`sources` it adds an additional `source` tag above any `source` tag rendered by a given `sources` element.

### lossless
Enable lossless compression for webp images. If you find your webp images lacking in quality compared to jpg/png images, enable
this option to overwrite default settings for ImageMagick/GraphicsMagick.
this option to overwrite default settings for ImageMagick/GraphicsMagick.

### variants and sizes
Adds multiple variants of an image with different image sizes, optionally add a sizes-attribute to image tags:
Expand Down Expand Up @@ -96,7 +102,7 @@ Add a CSS class used for the `picture` element (if rendered using `<picture>`).
## TypoScript Settings

### In general
The following attributes can also be set in TypoScript as defaults for your whole site: `addWebp`, `useRetina`.
The following attributes can also be set in TypoScript as defaults for your whole site: `useRetina`, `addAvif`, `addWebp`.
A default setting can be overridden for each usage of the ViewHelper by setting the corresponding attribute.

### retina
Expand Down Expand Up @@ -128,8 +134,8 @@ You can include a test configuration to see the ViewHelper in your test instance

`@import 'EXT:picture/Configuration/TypoScript/test.typoscript'`

This configuration enables frontend rendering of the test file with lots of different rendering examples using the
page type `1573387706874`.
This configuration enables frontend rendering of the test file with lots of different rendering examples using the
page type `1573387706874`.

`https://your.local.test.environment/?type=1573387706874`

Expand All @@ -142,4 +148,4 @@ This extension was created by Andreas Hämmerl and David Steeb in 2019 for b13 G

[Find more TYPO3 extensions we have developed](https://b13.com/useful-typo3-extensions-from-b13-to-you) that help us
deliver value in client projects. As part of the way we work, we focus on testing and best practices ensuring long-term
performance, reliability, and results in all our code.
performance, reliability, and results in all our code.

0 comments on commit f06edf5

Please sign in to comment.