Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Add AVIF image format @ <picture> #40

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
33 changes: 32 additions & 1 deletion 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 @@ -150,9 +157,14 @@ public function render(): string
$sourceOutputs = [];
$tag = $this->buildSingleTag('source', $sourceConfiguration);
$sourceOutputs[] = $tag->render();
$type = $tag->getAttribute('type');

// 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()) {
$tag = $this->addWebpImage($sourceConfiguration);
array_unshift($sourceOutputs, $tag->render());
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.
95 changes: 9 additions & 86 deletions Resources/Public/Icons/Extension.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.