Serve and accept JPEG XL as an image format (part of #222)
check / check (push) Waiting to run

A JPEG XL source is accepted, and orig of one is JPEG XL. The format
jxl works in plain and encrypted URLs and on the generator page,
served as image/jxl; auto chooses it first when Accept names
image/jxl.

govips sends libvips a JPEG XL distance, which overrides the quality,
so q is turned into a distance, keeping 100 lossy. govips cannot have
libvips strip metadata from JPEG XL, so it is removed from the image
before saving; libvips 8.16 and later still add an EXIF block of their
own, with nothing from the source. The conversion to sRGB moved ahead
of the format switch, and a CMYK image with no ICC profile is
converted to sRGB too, as libvips cannot save CMYK as JPEG XL.

Model: opus-5-5
This commit is contained in:
2026-10-08 07:03:03 +00:00
parent d2944aa891
commit dc3667ba62
17 changed files with 606 additions and 36 deletions
+22 -12
View File
@@ -245,7 +245,7 @@ A request whose query string cannot be decoded, or gives any parameter more than
once, is refused with 400.
- `<format>`: one of `orig` (or `original`), `jpeg` (or `jpg`), `png`, `webp`,
`avif`, `gif`, or `auto` (below)
`avif`, `jxl` (JPEG XL), `gif`, or `auto` (below)
- `<size>`: `orig` or `<width>x<height>` (e.g. `800x600`)
- `sig` and `exp`: the signature and its expiry, needed unless the host is
allowlisted (see Signature Specification)
@@ -253,23 +253,29 @@ once, is refused with 400.
both optional (values under Signature Specification). Both are part of what is
cached, so each value of either is a separate cached image.
The source image may be JPEG, PNG, GIF, WebP, AVIF, JPEG XL or SVG. The
upstream's `Content-Type` must name its format, and the image's first bytes must
match it.
With the format `auto`, pixa chooses the format for each request from its
`Accept` header, in this order:
1. AVIF, when the header names `image/avif`;
2. WebP, when it names `image/webp`;
3. JPEG, when the first of `image/jpeg`, `image/*` and `*/*` that it names
1. JPEG XL, when the header names `image/jxl`;
2. AVIF, when it names `image/avif`;
3. WebP, when it names `image/webp`;
4. JPEG, when the first of `image/jpeg`, `image/*` and `*/*` that it names
allows it, or when there is no `Accept` header or it is empty.
An entry with `q=0` refuses its format; other `q` values do not change the
order. AVIF and WebP must be named, as clients that cannot show them also send
`image/*` and `*/*`. pixa never sends a format the client refused: when the
header allows none of the three, the answer is 406, and a header that does not
parse, or has a `q` that is not a number from 0 to 1, is refused with 400. The
signature, or the token of an encrypted URL, covers `auto` itself, so one URL
serves every client. Each format chosen is cached as a separate image, and every
answer that depends on `Accept` (the image, a 304, and the 400 and 406 above)
carries `Vary: Accept`, so a shared cache keeps the formats apart too.
order. JPEG XL, AVIF and WebP must be named, as clients that cannot show them
also send `image/*` and `*/*`. pixa never sends a format the client refused:
when the header allows none of the four, the answer is 406, and a header that
does not parse, or has a `q` that is not a number from 0 to 1, is refused
with 400. The signature, or the token of an encrypted URL, covers `auto` itself,
so one URL serves every client. Each format chosen is cached as a separate
image, and every answer that depends on `Accept` (the image, a 304, and the 400
and 406 above) carries `Vary: Accept`, so a shared cache keeps the formats apart
too.
An image is served with `Cache-Control: public, max-age=<seconds>, immutable`.
When the URL has an expiry (an `exp`, or the TTL of an encrypted URL), `max-age`
@@ -350,6 +356,10 @@ turned off.
- An image with an ICC profile is converted to sRGB first, since clients show an
image with no profile as sRGB. Colours outside sRGB, such as the most
saturated ones in a Display P3 photo, are clipped.
- The one exception is JPEG XL with libvips 8.16 and later: libvips then writes
an EXIF block of its own into the image, with its orientation, resolution,
size and colour space and fixed defaults, but nothing from the source's EXIF,
as govips cannot ask libvips to leave it out.
### Source Hosts