Skip to content

Change default file type to AVIF (Baseline 2024) #205

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

Open
wants to merge 1 commit into
base: main
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
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.x"
- run: |
sudo apt update && sudo apt install libavif-dev libwebp-dev libjpeg8-dev -y
if: matrix.os == 'ubuntu-latest'
- run: brew install libavif libjpeg libraqm libtiff little-cms2 openjpeg webp
if: matrix.os == 'macos-latest'
- run: pacman -S \
mingw-w64-x86_64-gcc \
mingw-w64-x86_64-libjpeg-turbo \
mingw-w64-x86_64-zlib \
mingw-w64-x86_64-libtiff \
mingw-w64-x86_64-freetype \
mingw-w64-x86_64-lcms2 \
mingw-w64-x86_64-libwebp \
mingw-w64-x86_64-openjpeg2 \
mingw-w64-x86_64-libimagequant \
mingw-w64-x86_64-libraqm \
mingw-w64-x86_64-libavif
if: matrix.os == 'windows-latest'
- run: 'python -m pip install --upgrade Pillow --no-binary :all:'
- run: python -m pip install .[test]
- run: python -m pytest
- uses: codecov/codecov-action@v5
Expand Down Expand Up @@ -88,6 +107,8 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: sudo apt update && sudo apt install libavif-dev libwebp-dev libjpeg8-dev -y
- run: 'python -m pip install --upgrade Pillow --no-binary :all:'
- run: python -m pip install .[test]
- run: python -m pip install django~=${{ matrix.django-version }}.0
- run: python -m pytest
Expand Down Expand Up @@ -115,6 +136,8 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: sudo apt update && sudo apt install libavif-dev libwebp-dev libjpeg8-dev -y
- run: 'python -m pip install --upgrade Pillow --no-binary :all:'
- run: python -m pip install .[test]
- run: python -m pip install django~=${{ matrix.django-version }}.0
- run: python -m pytest
Expand Down Expand Up @@ -142,6 +165,8 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: 3.x
- run: sudo apt update && sudo apt install libavif-dev libwebp-dev libjpeg8-dev -y
- run: 'python -m pip install --upgrade Pillow --no-binary :all:'
- name: Install redis
if: matrix.extras == 'dramatiq' || matrix.extras == 'django-rq'
run: sudo apt install -y redis-server
Expand Down
86 changes: 45 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

Responsive cross-browser image library using modern codes like AVIF & WebP.

* responsive web images using the [picture](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) tag
* native grid system support
* serve files with or without a CDN
* placeholders for local development
* migration support
* async image processing for [Celery], [Dramatiq] or [Django RQ][django-rq]
* [DRF] support
- responsive web images using the [picture](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) tag
- native grid system support
- serve files with or without a CDN
- placeholders for local development
- migration support
- async image processing for [Celery], [Dramatiq] or [Django RQ][django-rq]
- [DRF] support

[![PyPi Version](https://img.shields.io/pypi/v/django-pictures.svg)](https://pypi.python.org/pypi/django-pictures/)
[![Test Coverage](https://codecov.io/gh/codingjoe/django-pictures/branch/main/graph/badge.svg)](https://codecov.io/gh/codingjoe/django-pictures)
Expand All @@ -21,8 +21,9 @@ Responsive cross-browser image library using modern codes like AVIF & WebP.
Before you start, it can be a good idea to understand the fundamentals of
[responsive images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images).

Once you get a feeling how complicated things can get with all device types, you'll probably find
a new appreciation for this package and are ready to adopt in you project :)
Once you get a feeling how complicated things can get with all device types,
you'll probably find a new appreciation for this package,
and are ready to adopt in your project :)

```python
# models.py
Expand All @@ -41,12 +42,13 @@ class Profile(models.Model):
```

The keyword arguments `m=6 l=4` define the columns the image should take up in
a grid at a given breakpoint. So in this example, the image will take up
6 columns on medium screens and 4 columns on large screens. You can define your
grid and breakpoints as you want, refer to the [grid columns](#grid-columns) and
a grid at a given breakpoint. So in this example, the image will take up
six columns on medium screens and four columns on large screens. You can define
your grid and breakpoints as you want, refer to the [grid columns](#grid-columns) and
[breakpoints](#breakpoints) sections.

The template above will render into:

```html
<picture class="my-picture">
<source type="image/webp"
Expand Down Expand Up @@ -89,7 +91,7 @@ PICTURES = {
},
"GRID_COLUMNS": 12,
"CONTAINER_WIDTH": 1200,
"FILE_TYPES": ["WEBP"],
"FILE_TYPES": ["AVIF"],
"PIXEL_DENSITIES": [1, 2],
"USE_PLACEHOLDERS": True,
"QUEUE_NAME": "pictures",
Expand Down Expand Up @@ -128,19 +130,16 @@ Although the `picture`-tag is [adequate for most use-cases][caniuse-picture],
some remain, where a single `img` tag is necessary. Notably in email, where
[most clients do support WebP][caniemail-webp] but not [srcset][caniemail-srcset].
The template tag `img_url` returns a single size image URL.
In addition to the ratio you will need to define the `file_type`
In addition to the ratio, you will need to define the `file_type`
as well as the `width` (absolute width in pixels).

Sadly, AVIF support [requires a little more setup][libavif-install].

```html
{% load pictures %}
<img src="{% img_url profile.picture ratio="3/2" file_type="webp" width=800 %}" alt="profile picture">
<img src="{% img_url profile.picture ratio='3/2' file_type='webp' width=800 %}" alt="profile picture">
```

[caniuse-picture]: https://caniuse.com/picture
[caniemail-webp]: https://www.caniemail.com/features/image-webp/
[caniemail-srcset]: https://www.caniemail.com/features/html-srcset/

## Config

### Aspect ratios
Expand Down Expand Up @@ -172,17 +171,17 @@ class Profile(models.Model):
If you don't specify an aspect ratio or None in your template, the image will be
served with the original aspect ratio of the file.

You may only use aspect ratios in templates, that have been defined on the model.
You may only use aspect ratios in templates that have been defined on the model.
The model `aspect_ratios` will default to `[None]`, if other value is provided.

### Breakpoints

You may define your own breakpoints, they should be identical to the ones used
in your css library. Simply override the `PICTURES["BREAKPOINTS"]` setting.
You may define your own breakpoints they should be identical to the ones used
in your CSS library. This can be achieved by overriding the `PICTURES["BREAKPOINTS"]` setting.

### Grid columns

Grids are so common in web design, that they even made it into CSS.
Grids are so common in web design that they even made it into CSS.
We default to 12 columns, but you can override this setting, via the
`PICTURES["GRID_COLUMNS"]` setting.

Expand All @@ -196,13 +195,14 @@ You may also set it to `None`, should you not use a container.

### File types

Unless you still services IE11 clients, you should be fine serving just
[WebP](https://caniuse.com/webp). Sadly, [AVIF](https://caniuse.com/avif)
(WebP's successor) is
[not yet supported by Pillow](https://github.com/python-pillow/Pillow/pull/5201).
[AVIF](https://caniuse.com/avif) ([WebP](https://caniuse.com/webp)'s successor)
is the best and most efficient image format available today. It is part of
Baseline 2024 and is supported by all major browsers. Additionally, most modern
devices will have hardware acceleration for AVIF decoding. This will not only
reduce network IO but speed up page rendering.

If you are serving IE11 use add `JPEG` to the list. Beware though, that this may
drastically increase you storage needs.
Should you still serve IE11, use add `JPEG` to the list. But, beware, this may
drastically increase your storage needs.

### Pixel densities

Expand All @@ -224,13 +224,11 @@ processor, should you need to do some custom processing.
## Migrations

Django doesn't support file field migrations, but we do.
You can simply auto create the migration and replace Django's
You can auto create the migration and replace Django's
`AlterField` operation with `AlterPictureField`. That's it.

You can follow [the example][migration] in our test app, to see how it works.

[migration]: tests/testapp/migrations/0002_alter_profile_picture.py

## Contrib

### Django Rest Framework ([DRF])
Expand All @@ -254,10 +252,10 @@ from rest_framework import serializers
from pictures.contrib.rest_framework import PictureField

class PictureSerializer(serializers.Serializer):
picture = PictureField(aspect_ratios=["16/9"], file_types=["WEBP"])
picture = PictureField(aspect_ratios=["16/9"], file_types=["AVIF"])
```

You also may provide optional GET parameters to the serializer,
You also may provide optional GET parameters to the serializer
to specify the aspect ratio and breakpoints you want to include in the response.
The parameters are prefixed with the `fieldname_`
to avoid conflicts with other fields.
Expand Down Expand Up @@ -303,7 +301,7 @@ Should you use a CDN, or some other external image processing service, you can
set this up in two simple steps:

1. Override `PICTURES["PROCESSOR"]` to disable the default processing.
2. Override `PICTURES["PICTURE_CLASS"]` implement any custom behavior.
1. Override `PICTURES["PICTURE_CLASS"]` implement any custom behavior.

```python
# settings.py
Expand All @@ -317,10 +315,11 @@ The `MyPicture`class should implement the url property, which returns the URL
of the image. You may use the `Picture` class as a base class.

Available attributes are:
* `parent_name` - name of the source file uploaded to the `PictureField`
* `aspect_ratio` - aspect ratio of the output image
* `width` - width of the output image
* `file_type` - file type of the output image

- `parent_name` - name of the source file uploaded to the `PictureField`
- `aspect_ratio` - aspect ratio of the output image
- `width` - width of the output image
- `file_type` - format of the output image

```python
# path/to.py
Expand All @@ -337,7 +336,12 @@ class MyPicture(Picture):
)
```

[drf]: https://www.django-rest-framework.org/
[caniemail-srcset]: https://www.caniemail.com/features/html-srcset/
[caniemail-webp]: https://www.caniemail.com/features/image-webp/
[caniuse-picture]: https://caniuse.com/picture
[celery]: https://docs.celeryproject.org/en/stable/
[dramatiq]: https://dramatiq.io/
[django-rq]: https://github.com/rq/django-rq
[dramatiq]: https://dramatiq.io/
[drf]: https://www.django-rest-framework.org/
[libavif-install]: https://pillow.readthedocs.io/en/latest/installation/building-from-source.html#external-libraries
[migration]: tests/testapp/migrations/0002_alter_profile_picture.py
2 changes: 1 addition & 1 deletion pictures/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def get_settings():
},
"GRID_COLUMNS": 12,
"CONTAINER_WIDTH": 1200,
"FILE_TYPES": ["WEBP"],
"FILE_TYPES": ["AVIF"],
"PIXEL_DENSITIES": [1, 2],
"USE_PLACEHOLDERS": settings.DEBUG,
"QUEUE_NAME": "pictures",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ classifiers = [
"Framework :: Django :: 5.2",
]
requires-python = ">=3.9"
dependencies = ["django>=4.2.0", "pillow>=9.0.0"]
dependencies = ["django>=4.2.0", "pillow>=11.2.0"]

[project.optional-dependencies]
test = [
Expand Down
2 changes: 1 addition & 1 deletion tests/contrib/test_cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_delete(self, stub_worker, image_upload_file):
stub_worker.join()

name = obj.picture.name
path = obj.picture.aspect_ratios["16/9"]["WEBP"][100].path
path = obj.picture.aspect_ratios["16/9"]["AVIF"][100].path
assert default_storage.exists(name)
assert path.exists()
with transaction.atomic(get_using(obj)):
Expand Down
Loading
Loading