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

AVIF support for wagtail #10657

Merged
merged 4 commits into from Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.txt
Expand Up @@ -25,6 +25,8 @@ Changelog
* Update link/document rich text tooltips for consistency with the inline toolbar (Albina Starykova)
* Increase the contrast between the rich text / StreamField block picker and the page in dark mode (Albina Starykova)
* Purge revisions of non-page models in `purge_revisions` command (Sage Abdullah)
* Add support for AVIF images (Aman Pandey)
* Change the default WebP quality to 80 to match AVIF (Aman Pandey)
* Fix: Prevent choosers from failing when initial value is an unrecognised ID, e.g. when moving a page from a location where `parent_page_types` would disallow it (Dan Braghis)
* Fix: Move comment notifications toggle to the comments side panel (Sage Abdullah)
* Fix: Remove comment button on InlinePanel fields (Sage Abdullah)
Expand Down
8 changes: 6 additions & 2 deletions docs/advanced_topics/images/image_file_formats.md
Expand Up @@ -12,6 +12,9 @@ image formats and let the browser choose the one it prefers. For example:
{% load wagtailimages_tags %}

<picture>
{% image myimage width-1000 format-avif as image_avif %}
<source srcset="{{ image_avif.url }}" type="image/avif">

{% image myimage width-1000 format-webp as image_webp %}
<source srcset="{{ image_webp.url }}" type="image/webp">

Expand All @@ -24,7 +27,7 @@ image formats and let the browser choose the one it prefers. For example:

### Customising output formats

By default all `bmp` and `webp` images are converted to the `png` format
By default, all `avif`, `bmp` and `webp` images are converted to the `png` format
when no image output format is given.

The default conversion mapping can be changed by setting the
Expand All @@ -35,10 +38,11 @@ For example:

```python
WAGTAILIMAGES_FORMAT_CONVERSIONS = {
'avif': 'avif',
'bmp': 'jpeg',
'webp': 'webp',
}
```

will convert `bmp` images to `jpeg` and disable the default `webp`
will convert `bmp` images to `jpeg` and disable the default `avif` and `webp`
to `png` conversion.
2 changes: 1 addition & 1 deletion docs/reference/settings.md
Expand Up @@ -359,7 +359,7 @@ WAGTAILIMAGES_EXTENSIONS = ['png', 'jpg']
```

A list of allowed image extensions that will be validated during image uploading.
If this isn't supplied, all of GIF, JPG, JPEG, PNG, WEBP are allowed.
If this isn't supplied, all of AVIF, GIF, JPG, JPEG, PNG, WEBP are allowed.
Warning: this doesn't always ensure that the uploaded file is valid as files can
be renamed to have an extension no matter what data they contain.

Expand Down
9 changes: 8 additions & 1 deletion docs/releases/5.1.md
Expand Up @@ -36,13 +36,19 @@ The `boost` option on `SearchField`, to increase the ranking of search results t

### Extend Stimulus adoption

As part of tackling Wagtail’s technical debt and improving [CSP compatibility]([Title](https://github.com/wagtail/wagtail/issues/1288)), we have continued extending our usage of Stimulus, based on the plans laid out in [RFC 78: Adopt Stimulus](https://github.com/wagtail/rfcs/pull/78).
As part of tackling Wagtail’s technical debt and improving [CSP compatibility](https://github.com/wagtail/wagtail/issues/1288), we have continued extending our usage of Stimulus, based on the plans laid out in [RFC 78: Adopt Stimulus](https://github.com/wagtail/rfcs/pull/78).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦 I have disabled my "smart link paste" IDE feature, thanks for catching this.


* Migrate Tagit initialisation to a Stimulus Controller (LB (Ben) Johnston)
* Migrate legacy dropdown implementation to a Stimulus controller (Thibaud Colas)
* Migrate header search behaviour to `w-swap`, a Stimulus controller (LB (Ben) Johnston)
* Replace Bootstrap tooltips with a new `w-tooltip` Stimulus controller (LB (Ben) Johnston)

### AVIF image support

Wagtail now supports [AVIF](https://en.wikipedia.org/wiki/AVIF), a modern image format. We encourage all site implementers to consider using it to improve the performance of the sites and reduce their carbon footprint. For further details, see [image file format](image_file_formats), [output image format](output_image_format) and [image quality](image_quality).

This feature was developed by Aman Pandey as part of the Google Summer of Code program and a [partnership with the Green Web Foundation](https://www.thegreenwebfoundation.org/news/working-with-the-wagtail-community-on-the-summer-of-code/) and Green Coding Berlin, with support from Dan Braghis, Thibaud Colas, Sage Abdullah, Arne Tarara (Green Coding Berlin), and Chris Adams (Green Web Foundation).

### Other features

* Mark calls to `md5` as not being used for secure purposes, to avoid flagging on FIPS-mode systems (Sean Kelly)
Expand All @@ -64,6 +70,7 @@ As part of tackling Wagtail’s technical debt and improving [CSP compatibility]
* Update link/document rich text tooltips for consistency with the inline toolbar (Albina Starykova)
* Increase the contrast between the rich text / StreamField block picker and the page in dark mode (Albina Starykova)
* Purge revisions of non-page models in `purge_revisions` command (Sage Abdullah)
* Change the default WebP quality to 80 to match AVIF (Aman Pandey)

### Bug fixes

Expand Down
18 changes: 12 additions & 6 deletions docs/topics/images.md
Expand Up @@ -326,6 +326,7 @@ Wagtail may automatically change the format of some images when they are resized

- PNG and JPEG images don't change the format
- GIF images without animation are converted to PNGs
- AVIF images are converted to PNGs
- BMP images are converted to PNGs
- WebP images are converted to PNGs

Expand All @@ -340,11 +341,12 @@ For example, to make the tag always convert the image to a JPEG, use `format-jpe

You may also use `format-png` or `format-gif`.

### Lossless WebP
### Lossless AVIF and WebP

You can encode the image into lossless WebP format by using the `format-webp-lossless` filter:
You can encode the image into lossless AVIF or WebP format by using `format-avif-lossless` or `format-webp-lossless` filter respectively:

```html+django
{% image page.photo width-400 format-avif-lossless %}
{% image page.photo width-400 format-webp-lossless %}
```

Expand All @@ -369,17 +371,19 @@ representing the colour you would like to use:

## Image quality

Wagtail's JPEG and WebP image quality settings default to 85 (which is quite high).
Wagtail's JPEG image quality settings default to 85 (which is quite high). AVIF and WebP default to 80.
This can be changed either globally or on a per-tag basis.

### Changing globally

Use the `WAGTAILIMAGES_JPEG_QUALITY` and `WAGTAILIMAGES_WEBP_QUALITY` settings to change the global defaults of JPEG and WebP quality:
Use the `WAGTAILIMAGES_AVIF_QUALITY`, `WAGTAILIMAGES_JPEG_QUALITY` and `WAGTAILIMAGES_WEBP_QUALITY` settings to change
the global defaults of AVIF, JPEG and WebP quality:

```python
# settings.py

# Make low-quality but small images
WAGTAILIMAGES_AVIF_QUALITY = 50
WAGTAILIMAGES_JPEG_QUALITY = 40
WAGTAILIMAGES_WEBP_QUALITY = 45
```
Expand All @@ -402,16 +406,18 @@ You can read more about this command from [](wagtail_update_image_renditions)

### Changing per-tag

It's also possible to have different JPEG and WebP qualities on individual tags by using `jpegquality` and `webpquality` filters. This will always override the default setting:
It's also possible to have different AVIF, JPEG and WebP qualities on individual tags by using `avifquality`, `jpegquality` and `webpquality` filters. This will always override the default setting:

```html+django
{% image page.photo_avif width-400 avifquality-40 %}
{% image page.photo_jpeg width-400 jpegquality-40 %}
{% image page.photo_webp width-400 webpquality-50 %}
```

Note that this will have no effect on PNG or GIF files. If you want all images to be low quality, you can use this filter with `format-jpeg` or `format-webp` (which forces all images to output in JPEG or WebP format):
Note that this will have no effect on PNG or GIF files. If you want all images to be low quality, you can use this filter with `format-avif`, `format-jpeg` or `format-webp` (which forces all images to output in AVIF, JPEG or WebP format):

```html+Django
{% image page.photo width-400 format-avif avifquality-40 %}
{% image page.photo width-400 format-jpeg jpegquality-40 %}
{% image page.photo width-400 format-webp webpquality-50 %}
```
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -30,7 +30,7 @@
"Pillow>=9.1.0,<11.0.0",
"beautifulsoup4>=4.8,<4.12",
"html5lib>=0.999,<2",
"Willow>=1.5,<1.6",
"Willow[heif]>=1.6,<1.7",
"requests>=2.11.1,<3.0",
"l18n>=2018.5",
"openpyxl>=3.0.10,<4.0",
Expand Down
6 changes: 5 additions & 1 deletion wagtail/images/fields.py
Expand Up @@ -12,7 +12,9 @@

def get_allowed_image_extensions():
return getattr(
settings, "WAGTAILIMAGES_EXTENSIONS", ["gif", "jpg", "jpeg", "png", "webp"]
settings,
"WAGTAILIMAGES_EXTENSIONS",
["avif", "gif", "jpg", "jpeg", "png", "webp"],
)


Expand Down Expand Up @@ -201,5 +203,7 @@ def image_format_name_to_content_type(image_format_name):
return "image/tiff"
elif image_format_name == "webp":
return "image/webp"
elif image_format_name == "avif":
return "image/avif"
else:
raise ValueError("Unknown image format name")
17 changes: 15 additions & 2 deletions wagtail/images/image_operations.py
Expand Up @@ -386,6 +386,17 @@ def run(self, willow, image, env):
env["jpeg-quality"] = self.quality


class AvifQualityOperation(FilterOperation):
def construct(self, quality):
self.quality = int(quality)

if self.quality > 100:
raise ValueError("AVIF quality must not be higher than 100")

def run(self, willow, image, env):
env["avif-quality"] = self.quality


class WebPQualityOperation(FilterOperation):
def construct(self, quality):
self.quality = int(quality)
Expand All @@ -402,8 +413,10 @@ def construct(self, format, *options):
self.format = format
self.options = options

if self.format not in ["jpeg", "png", "gif", "webp"]:
raise ValueError("Format must be either 'jpeg', 'png', 'gif', or 'webp'")
if self.format not in ["jpeg", "png", "gif", "webp", "avif"]:
raise ValueError(
"Format must be either 'jpeg', 'png', 'gif', 'webp' or 'avif'"
)

def run(self, willow, image, env):
env["output-format"] = self.format
Expand Down
16 changes: 15 additions & 1 deletion wagtail/images/models.py
Expand Up @@ -49,6 +49,7 @@


IMAGE_FORMAT_EXTENSIONS = {
"avif": ".avif",
"jpeg": ".jpg",
"png": ".png",
"gif": ".gif",
Expand Down Expand Up @@ -917,6 +918,7 @@ def run(self, image: AbstractImage, output: BytesIO, source: File = None):
else:
# Convert bmp and webp to png by default
default_conversions = {
"avif": "png",
"bmp": "png",
"webp": "png",
}
Expand Down Expand Up @@ -962,9 +964,21 @@ def run(self, image: AbstractImage, output: BytesIO, source: File = None):
elif "webp-quality" in env:
quality = env["webp-quality"]
else:
quality = getattr(settings, "WAGTAILIMAGES_WEBP_QUALITY", 85)
quality = getattr(settings, "WAGTAILIMAGES_WEBP_QUALITY", 80)

return willow.save_as_webp(output, quality=quality)
elif output_format == "avif":
zerolab marked this conversation as resolved.
Show resolved Hide resolved
# Allow changing of AVIF compression quality
if (
"output-format-options" in env
and "lossless" in env["output-format-options"]
):
return willow.save_as_avif(output, lossless=True)
zerolab marked this conversation as resolved.
Show resolved Hide resolved
elif "avif-quality" in env:
quality = env["avif-quality"]
else:
quality = getattr(settings, "WAGTAILIMAGES_AVIF_QUALITY", 80)
return willow.save_as_avif(output, quality=quality)
elif output_format == "svg":
return willow.save_as_svg(output)
raise UnknownOutputImageFormatError(
Expand Down
4 changes: 2 additions & 2 deletions wagtail/images/tests/test_admin_views.py
Expand Up @@ -571,7 +571,7 @@ def test_add_svg_denied(self):
response,
"form",
"file",
"Not a supported image format. Supported formats: GIF, JPG, JPEG, PNG, WEBP.",
"Not a supported image format. Supported formats: AVIF, GIF, JPG, JPEG, PNG, WEBP.",
)

@override_settings(WAGTAILIMAGES_EXTENSIONS=["svg"])
Expand Down Expand Up @@ -2090,7 +2090,7 @@ def test_select_format_flag_after_upload_form_error_bad_extension(self):
response,
"form",
"file",
"Not a supported image format. Supported formats: GIF, JPG, JPEG, PNG, WEBP.",
"Not a supported image format. Supported formats: AVIF, GIF, JPG, JPEG, PNG, WEBP.",
)

# the action URL of the re-rendered form should include the select_format=true parameter
Expand Down