Add WebP support

WebP is an image format employing both lossy and lossless compression. The format is a new open standard for lossy compressed true-color graphics on the web, producing much smaller files of comparable image quality to the older JPEG scheme.

This format is currently not supported by browsers in the same way as, for instance JPEG, PNG or GIF. This means that it can not be used as a replacement inside an <img src="..." /> tag. A list of browsers supporting WebP can be found on caniuse.

Therefore, we can not use WebP as a drop-in replacement for JPEG or PNG, but instead must offer the image alongside with one of our well-known formats. To achieve this, we use the Picture element such as:

<picture>
  <source srcset="/path/to/image.webp" type="image/webp">
  <img src="/path/to/image.jpg">
</picture>

This means that must continue to keep the thumbnailed images in either JPEG or PNG format. Every time an image is thumbnailed, a corresponding image must be generated using the WebP format. We can use this short function:

def store_as_webp(sender, **kwargs):
    webp_path = sender.storage.path('.'.join([sender.name, 'webp']))
    sender.image.save(webp_path, 'webp')

We then connect this funtion to the signal handler offerd by Easy-Thumbnails. A good place to register that handler is the ready() method inside our AppConfig:

...

def ready(self):
    from easy_thumbnails.signals import thumbnail_created
    thumbnail_created.connect(store_as_webp)

The last thing to do, is to rewrite the Django templates used to render image elements:

{% load thumbnail %}
...
<picture>
  {% thumbnail image 400x300 as thumb %}
  <source srcset="{{ thumb.url }}.webp" type="image/webp" />
  <img src="{{ thumb.url }}" width="{{ thumb.width }}" height="{{ thumb.height }}" />
</picture>

Remark

In the future, Easy Thumbnails might support WebP natively. This however means that it must be usable as <img ...> -tag, supported by all browsers, and fully integrated into tools such as django-filer.

Until that happens, I reccomend to proceed with the workarround described here.