getzola / zola

A fast static site generator in a single binary with everything built-in. https://www.getzola.org
https://www.getzola.org
MIT License
13.5k stars 943 forks source link

Feature request: Add AVIF support. #1202

Open BezPowell opened 3 years ago

BezPowell commented 3 years ago

Adding this as a feature request now as image-rs seem to be well advanced with their avif support: https://github.com/image-rs/image/issues/1152 The original forum thread for reference: https://zola.discourse.group/t/feature-request-add-support-for-avif-images-to-resize-image/538

I don't have much development time at the moment due to real life, but happy to take a crack at this at a later date if no one else has claimed it.

BezPowell commented 3 years ago

As of image v0.23.12 there is now an ImageOutputFormat::Avif option for using with write_to. So far though, it is only possible to encode avif images with a quality setting of 100, which results in really large files. When setting the quality becomes available, I will have another stab at adding this to zola, reusing the quality setting from jpegs.

pawelurbanski commented 3 years ago

Maybe this will be useful/helpful: https://github.com/kornelski/cavif-rs

BezPowell commented 3 years ago

Maybe this will be useful/helpful: https://github.com/kornelski/cavif-rs

That's the very crate image-rs are using for avif encoding, seems to be a really good choice for avif support. My pr to allow creating an avif encoder with specified speed and quality has just been merged, so hopefully we'll be able to add support to Zola soon.

pawelurbanski commented 3 years ago

I was thinking about the following feature. It very likely should be filed as an idea to Kornel, the cavif-rs author. When converting files for the web, we very likely need multiple sizes. It would be useful to be able to pass a list/array of resolutions to the convert call.

Here is a rough example:

  1. image.jpg is 4000x4000 pixels,
  2. We need it in 3 different resolutions,
  3. A possible parameter would be: "3000x3000, 2000x2000, 1000x1000"

We could split it by a comma or use a different separator. The individual resolutions use an x as a separator as all the libraries tend to do. Placing a 0 (zero) after the x would mean to keep the height propotional or the other way around if the first part to the left from x would be 0 (zero). I guesss that once we have the bit stream for the original image we can use it to generate different resolutions or not - I'm not the expert in such matters. Obviously this batch size creation would be very beneficial for jpeg resizing as well.

skyfaller commented 3 years ago

I was thinking about the following feature. It very likely should be filed as an idea to Kornel, the cavif-rs author. When converting files for the web, we very likely need multiple sizes. It would be useful to be able to pass a list/array of resolutions to the convert call.

I already asked for resizing to be supported, and kornel said no, you should use ImageMagick instead: https://github.com/kornelski/cavif-rs/issues/7

I don't know if there is some ImageMagick equivalent written in Rust, but finding or creating such a thing with support for AVIF may be worthwhile.

BezPowell commented 3 years ago

I was thinking about the following feature. It very likely should be filed as an idea to Kornel, the cavif-rs author. When converting files for the web, we very likely need multiple sizes. It would be useful to be able to pass a list/array of resolutions to the convert call.

I already asked for resizing to be supported, and kornel said no, you should use ImageMagick instead: kornelski/cavif-rs#7

I don't know if there is some ImageMagick equivalent written in Rust, but finding or creating such a thing with support for AVIF may be worthwhile.

As far as I am aware the resizing and encoding stages are separate in the image-rs crate. Zola's resize_image() function will load the source image, resize it, and then re-encode to whatever format you specify. On my site I'm currently doing this for images with srcsets, loading the sizes from a json file.

{% set sizes = load_data(path="templates/includes/image_sizes.json", format="json") %}
<picture class="banner">
  {#- Jpeg srcset -#}
  <source type="image/jpeg" srcset="
    {%- for size in sizes %}
      {{- resize_image(path=src, width=size.width, height=size.height) }} {{ size.width }}w
      {%- if not loop.last %}, {% endif -%}
    {% endfor -%}
    " sizes="100vw" />

  {#- Jpeg fallback -#}
  {% set fallback = sizes | last %}
  <img src="{{ resize_image(path=src, width=fallback.width, height=fallback.height) }}" width="{{ fallback.width }}" height="{{ fallback.height }}" alt="" loading="eager" />
</picture>

As soon as avif is added it should be possible to add an additional source element with a type of image/avif and setting the format on resize_image to avif. The above example is simplified from a banner image, so the alt text is empty being purely decorative and loading is set to lazy as it appears above the fold.

pawelurbanski commented 3 years ago

As far as I am aware the resizing and encoding stages are separate in the image-rs crate. Zola's resize_image() function will load the source image, resize it, and then re-encode to whatever format you specify. Keeping in mind what I cited above we should keep in mind that avif compression is quite demanding and takes much more time than resizing a jpg file or encoding to webp. That is why I was thinking if it would be possible to encode the original image and then resize it to different dimensions from that encoded source.

BezPowell commented 3 years ago

Keeping in mind what I cited above we should keep in mind that avif compression is quite demanding and takes much more time than resizing a jpg file or encoding to webp. That is why I was thinking if it would be possible to encode the original image and then resize it to different dimensions from that encoded source.

I'm not sure that would be possible?

As avif, jpeg and png etc are just ways of compressing the raw bitmap data, the image needs to be decoded back to raw data before it can be resized. Any transforms are then made upon the uncompressed image and would need to be re-encoded back to avif before saving. If you took a source image and resized it to 3 different sizes, each one of those new images would have to be encoded separately.

I could be totally wrong on this, as I know fliff had a similar feature where it was encoded in such a way that each slice of the image could be used to construct a smaller variant, but I don't think avif supports any form of progressive encoding.