Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc3667ba62 |
+7
-9
@@ -17,8 +17,8 @@ COPY . .
|
||||
RUN golangci-lint run --config .golangci.yml ./...
|
||||
|
||||
# Test phase. script/test builds it alone.
|
||||
# golang:1.25.4-alpine3.22, 2026-02-25; the runtime stage uses Alpine 3.22 too
|
||||
FROM golang:1.25.4-alpine3.22@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS test
|
||||
# golang:1.25.4-alpine, 2026-02-25
|
||||
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS test
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
@@ -40,8 +40,8 @@ RUN go test -count=1 -timeout 90s -race -cover ./... || \
|
||||
# Build stage. Nothing is wanted from the two phases above: these copies
|
||||
# make BuildKit build them first, so this stage runs only when lint and
|
||||
# test passed.
|
||||
# golang:1.25.4-alpine3.22, 2026-02-25; the runtime stage uses Alpine 3.22 too
|
||||
FROM golang:1.25.4-alpine3.22@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS builder
|
||||
# golang:1.25.4-alpine, 2026-02-25
|
||||
FROM golang:1.25.4-alpine@sha256:d3f0cf7723f3429e3f9ed846243970b20a2de7bae6a5b66fc5914e228d831bbb AS builder
|
||||
|
||||
COPY --from=lint /src/go.sum /dev/null
|
||||
COPY --from=test /src/go.sum /dev/null
|
||||
@@ -77,11 +77,9 @@ RUN version="${VERSION:-$(git describe --tags --always)}"; \
|
||||
-o /pixad ./cmd/pixad
|
||||
|
||||
# Runtime stage, and the last one: a plain `docker build .` builds this
|
||||
# stage and what it depends on, and nothing else. It must use the Alpine
|
||||
# release the golang image above is based on, so that pixad runs against
|
||||
# the libvips and musl it was built with.
|
||||
# alpine:3.22, 2026-10-08
|
||||
FROM alpine:3.22@sha256:5291449c3df73caf6ed85e649dec1b9e818b39a5d8c871e97afc13e9cd5e8fa8
|
||||
# stage and what it depends on, and nothing else.
|
||||
# alpine:3.21, 2026-02-25
|
||||
FROM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
|
||||
|
||||
# Install runtime dependencies only. vips-jxl is libvips' JPEG XL
|
||||
# support, without which pixad does not start.
|
||||
|
||||
@@ -88,7 +88,7 @@ or with 1 when images were still being processed after those 5 seconds or
|
||||
another part of pixa failed to stop. A request not finished by then is cut off.
|
||||
`docker stop` waits 10 seconds before it kills the container.
|
||||
|
||||
Outside Docker, pixa needs libvips (the image has 8.16) and libheif to run, as
|
||||
Outside Docker, pixa needs libvips (the image has 8.15) and libheif to run, as
|
||||
it uses libvips through CGO. pixad does not start unless libvips has its JPEG XL
|
||||
support, which on Alpine is the `vips-jxl` package and which the nix and brew
|
||||
packages of libvips include, as do the apt ones from Debian 12 and Ubuntu 24.04
|
||||
@@ -357,9 +357,9 @@ turned off.
|
||||
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, as govips cannot ask libvips to leave
|
||||
it out. It holds the image's size and otherwise fixed values, such as an
|
||||
orientation of 1 and a resolution of 72 dpi, and nothing from the source.
|
||||
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
|
||||
|
||||
|
||||
@@ -40,17 +40,12 @@ P2: security: per-IP rate limiting on the image routes
|
||||
libvips ignores the quality: pixa turns `q` into a distance with libvips' own
|
||||
formula, keeping 100 lossy, and removes the metadata from the image before
|
||||
saving, as govips cannot have libvips strip it from JPEG XL. libvips 8.16 and
|
||||
later still write an EXIF block of their own into JPEG XL, from the image's
|
||||
size, orientation and resolution, and fixed values; the image is upright and
|
||||
is given 72 dpi before the save, so the block holds nothing from the source.
|
||||
later still write an EXIF block of their own into JPEG XL (orientation,
|
||||
resolution, size, colour space, fixed defaults), with nothing from the source.
|
||||
An image with an ICC profile is converted to sRGB before the JPEG XL save too,
|
||||
and a CMYK image with none is converted to sRGB, as libvips cannot save CMYK
|
||||
as JPEG XL. libvips' default effort, 7, is kept. JPEG XL is not yet the
|
||||
default output.
|
||||
- 2026-10-08 pixad runs on the Alpine release it is built on (closes #229): the
|
||||
runtime stage of the `Dockerfile` uses `alpine:3.22`, the release of the
|
||||
`golang:1.25.4-alpine3.22` image that the test phase and the build stage use,
|
||||
so all three have libvips 8.16, where the runtime image had 8.15.
|
||||
- 2026-10-08 requests no longer wait behind eviction queries that read a whole
|
||||
table (closes #227): the new `cache_usage` table holds the total cache usage,
|
||||
kept up to date by triggers on `source_content` and `variant_content` in the
|
||||
|
||||
@@ -557,11 +557,6 @@ func (p *ImageProcessor) encode(
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// jxlResolution is the resolution every JPEG XL image is saved with, in
|
||||
// pixels per millimetre as libvips counts it: 72 dpi, what libvips gives a
|
||||
// JPEG that names none.
|
||||
const jxlResolution = 72 / 25.4
|
||||
|
||||
// exportJXL encodes img as JPEG XL at quality, with libvips' default effort
|
||||
// and without metadata. govips sends libvips a distance, the JPEG XL
|
||||
// encoder's own measure of quality, along with the quality, and libvips then
|
||||
@@ -579,7 +574,9 @@ func exportJXL(img *vips.ImageRef, quality int) ([]byte, error) {
|
||||
|
||||
// govips cannot make libvips strip metadata from JPEG XL, so it is
|
||||
// removed from the image itself. RemoveMetadata removes EXIF, XMP and
|
||||
// IPTC but keeps the ICC profile.
|
||||
// IPTC but keeps the ICC profile. libvips 8.16 and later still write an
|
||||
// EXIF block of their own: orientation, resolution, size, colour space
|
||||
// and fixed defaults.
|
||||
err := img.RemoveMetadata()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -590,22 +587,11 @@ func exportJXL(img *vips.ImageRef, quality int) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// libvips 8.16 and later still write an EXIF block of their own, from
|
||||
// the image's size, orientation and resolution, and fixed values. The
|
||||
// image is upright, so its orientation is 1, but its resolution is still
|
||||
// the source's.
|
||||
toSave, err := img.CopyChangingResolution(jxlResolution, jxlResolution)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer toSave.Close()
|
||||
|
||||
params := vips.NewJxlExportParams()
|
||||
params.Quality = quality
|
||||
params.Distance = jxlDistance(quality)
|
||||
|
||||
output, _, err := toSave.ExportJxl(params)
|
||||
output, _, err := img.ExportJxl(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -102,30 +102,6 @@ func TestImageProcessor_JPEGXLDropsSourceEXIF(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestImageProcessor_JPEGXLDropsSourceResolution verifies that the source's
|
||||
// resolution does not reach the EXIF block libvips 8.16 and later write into
|
||||
// JPEG XL. A JPEG XL image holds its resolution in that block alone, so the
|
||||
// resolution the output loads with is the block's.
|
||||
func TestImageProcessor_JPEGXLDropsSourceResolution(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// dpi-300.jpg is a flat 8x8 grey image with a resolution of 300 dpi.
|
||||
input, err := os.ReadFile("testdata/dpi-300.jpg")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read test JPEG: %v", err)
|
||||
}
|
||||
|
||||
output := processAndDecode(t, input, &Request{Format: FormatJXL})
|
||||
|
||||
// libvips gives the resolution in pixels per millimetre.
|
||||
xDPI := math.Round(output.ResX() * 25.4)
|
||||
yDPI := math.Round(output.ResY() * 25.4)
|
||||
|
||||
if xDPI == 300 || yDPI == 300 {
|
||||
t.Errorf("output resolution = %vx%v dpi, the source's", xDPI, yDPI)
|
||||
}
|
||||
}
|
||||
|
||||
// TestImageProcessor_JPEGXLAppliesEXIFOrientation verifies that a JPEG XL
|
||||
// output is turned upright, as TestImageProcessor_AppliesEXIFOrientation does
|
||||
// for PNG.
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 799 B |
Reference in New Issue
Block a user