Keep WebP, AVIF and JPEG XL encodes at the limits under 40 seconds (closes #272) #278

Merged
clawbot merged 2 commits from issue-272-encode-time-at-the-limits into next 2026-10-20 15:19:08 +02:00
Collaborator

WebP is now saved at effort 2 instead of libvips' default of 4, and JPEG XL at effort 4 instead of 7. AVIF was already at the lowest effort govips can set, so an AVIF output of more than 16,777,216 pixels (4096x4096) is refused with 400 before anything is decoded. No encode at the limits takes more than 40 seconds of the default downstream_timeout of 60.

Measured with the runtime image's libvips 8.16.1 on one thread, saving random pixels at 8 and 16 bits, qualities 1 to 100. With an alpha channel: WebP up to about 23 seconds at 8192x8192 (61 before), JPEG XL 27 (81), AVIF 34 at 4096x4096, 8192x2048 or 2048x8192 (96 at 8192x8192 and quality 85). Without one: 16, 13 and 22. AVIF is slowest at quality 90 to 99.

Not visible in the diff:

  • With fit=cover, libvips decodes the input while resizing, so outputDimensions now works out the output's dimensions first, and holds the existing fit=outside limit too. A new test shows a refused request decoded nothing, using inputs whose pixel data lacks the size in their header: a cut-off PNG, and the 4x4 testdata/red.avif with its header changed to 4097x4096.
  • With auto, a client whose Accept names image/avif but not image/jxl gets 400 for an output above the limit, not another format.

TestImageProcessor_WebPAtDefaultEffort, which required effort 4, is now TestImageProcessor_WebPAtEffort2, changed under sneak's ruling at #280 (comment).

Judgement call: reading the input moved into readInput to keep Process within the linter's length and complexity limits.

Model: opus-5-5

WebP is now saved at effort 2 instead of libvips' default of 4, and JPEG XL at effort 4 instead of 7. AVIF was already at the lowest effort govips can set, so an AVIF output of more than 16,777,216 pixels (4096x4096) is refused with 400 before anything is decoded. No encode at the limits takes more than 40 seconds of the default `downstream_timeout` of 60. Measured with the runtime image's libvips 8.16.1 on one thread, saving random pixels at 8 and 16 bits, qualities 1 to 100. With an alpha channel: WebP up to about 23 seconds at 8192x8192 (61 before), JPEG XL 27 (81), AVIF 34 at 4096x4096, 8192x2048 or 2048x8192 (96 at 8192x8192 and quality 85). Without one: 16, 13 and 22. AVIF is slowest at quality 90 to 99. Not visible in the diff: - With `fit=cover`, libvips decodes the input while resizing, so `outputDimensions` now works out the output's dimensions first, and holds the existing `fit=outside` limit too. A new test shows a refused request decoded nothing, using inputs whose pixel data lacks the size in their header: a cut-off PNG, and the 4x4 `testdata/red.avif` with its header changed to 4097x4096. - With `auto`, a client whose `Accept` names `image/avif` but not `image/jxl` gets 400 for an output above the limit, not another format. `TestImageProcessor_WebPAtDefaultEffort`, which required effort 4, is now `TestImageProcessor_WebPAtEffort2`, changed under sneak's ruling at https://git.eeqj.de/sneak/pixa/issues/280#issuecomment-151029. Judgement call: reading the input moved into `readInput` to keep `Process` within the linter's length and complexity limits. Model: opus-5-5
clawbot added the needs-review label 2026-10-15 22:21:36 +02:00
clawbot self-assigned this 2026-10-15 22:21:37 +02:00
Author
Collaborator

Question for sneak: TestImageProcessor_WebPAtDefaultEffort, a test on next, required WebP at effort 4, the setting #272 lowers. This PR changes it to require effort 2, as TestImageProcessor_WebPAtEffort2, and CLAUDE.md asks for your approval to change an existing test. The options are to approve the change, or to keep the old test and leave WebP at effort 4, which takes about 61 seconds at the limits with an alpha channel. I recommend approving it, since the test checks the setting and a new value needs a new check. The PR is built on that reading.

Model: opus-5-5

Question for sneak: `TestImageProcessor_WebPAtDefaultEffort`, a test on `next`, required WebP at effort 4, the setting https://git.eeqj.de/sneak/pixa/issues/272 lowers. This PR changes it to require effort 2, as `TestImageProcessor_WebPAtEffort2`, and `CLAUDE.md` asks for your approval to change an existing test. The options are to approve the change, or to keep the old test and leave WebP at effort 4, which takes about 61 seconds at the limits with an alpha channel. I recommend approving it, since the test checks the setting and a new value needs a new check. The PR is built on that reading. Model: opus-5-5
Author
Collaborator

Review failed: needs-rework.

  1. The AVIF encode times in the doc comment of MaxAVIFOutputPixels (internal/imageprocessor/imageprocessor.go), in the paragraph on encode times under "Routes" in README.md, in the new TODO.md entry and in the second commit's message give about 25 seconds as the longest an AVIF output at the limit takes with an alpha channel, and about 15 without one. Those are the times at quality 85 and 100 only. At quality 90 to 99, a 4096x4096, 8192x2048 or 2048x8192 output of random pixels takes about 31 seconds with an alpha channel and about 20 without, on one libvips thread with the runtime image's libvips. That is still under 40 seconds, so the limit holds, but the stated worst case is not the worst case, and the TODO.md entry's "at most about 27 seconds" is wrong. Acceptable: the AVIF times are measured across the whole quality range, and the slowest is what each of these places states.
  2. The branch: Gitea reports the PR as not mergeable with the current next, which has since added two entries at the top of Completed Steps in TODO.md. Acceptable: the branch rebased onto next, with this PR's entry kept once, at the top of Completed Steps.

Rule suppressed: the change to TestImageProcessor_WebPAtDefaultEffort waits on sneak's answer at #278 (comment) and is not counted as a finding.
Judgement call: an auto request above the AVIF limit, from a client whose Accept names image/avif but not image/jxl, is refused instead of being served another format. README.md says so, and it is not counted as a finding.
Partially verified: the encode times were measured with the vips command of the runtime image's libvips 8.16.1 on one thread, not through pixad.

Model: opus-5-5

Review failed: needs-rework. 1. The AVIF encode times in the doc comment of `MaxAVIFOutputPixels` (`internal/imageprocessor/imageprocessor.go`), in the paragraph on encode times under "Routes" in `README.md`, in the new `TODO.md` entry and in the second commit's message give about 25 seconds as the longest an AVIF output at the limit takes with an alpha channel, and about 15 without one. Those are the times at quality 85 and 100 only. At quality 90 to 99, a 4096x4096, 8192x2048 or 2048x8192 output of random pixels takes about 31 seconds with an alpha channel and about 20 without, on one libvips thread with the runtime image's libvips. That is still under 40 seconds, so the limit holds, but the stated worst case is not the worst case, and the `TODO.md` entry's "at most about 27 seconds" is wrong. Acceptable: the AVIF times are measured across the whole quality range, and the slowest is what each of these places states. 2. The branch: Gitea reports the PR as not mergeable with the current `next`, which has since added two entries at the top of Completed Steps in `TODO.md`. Acceptable: the branch rebased onto `next`, with this PR's entry kept once, at the top of Completed Steps. Rule suppressed: the change to `TestImageProcessor_WebPAtDefaultEffort` waits on sneak's answer at https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-143931 and is not counted as a finding. Judgement call: an `auto` request above the AVIF limit, from a client whose `Accept` names `image/avif` but not `image/jxl`, is refused instead of being served another format. `README.md` says so, and it is not counted as a finding. Partially verified: the encode times were measured with the `vips` command of the runtime image's libvips 8.16.1 on one thread, not through pixad. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-15 23:21:30 +02:00
clawbot force-pushed issue-272-encode-time-at-the-limits from cd65845c69 to 21c4838c4b 2026-10-16 00:47:04 +02:00 Compare
clawbot force-pushed issue-272-encode-time-at-the-limits from 21c4838c4b to 47a2c2360a 2026-10-16 00:47:27 +02:00 Compare
Author
Collaborator

Rework for #278 (comment):

  1. AVIF measured at qualities 1 to 100, at 4096x4096, 8192x2048 and 2048x8192, with and without an alpha channel, at 8 and 16 bits. The slowest is about 34 seconds with an alpha channel and 22 without, under 40, so the limit stays. MaxAVIFOutputPixels, avifEffort, "Routes" in README.md, the TODO.md entry, the fix commit's message and the PR body now give those times. WebP and JPEG XL, measured the same way, stay within the 23 and 27 seconds already given, and their comments now say so for any quality.
  2. Rebased onto the current next. The TODO.md entry appears once, at the top of Completed Steps.

Judgement call: the rework is folded into the fix commit so that its message gives the new times. The test commit is unchanged.
Partially verified: the times come from the vips command of the runtime image's libvips 8.16.1, run with the settings govips passes, not through pixad.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-144030: 1. AVIF measured at qualities 1 to 100, at 4096x4096, 8192x2048 and 2048x8192, with and without an alpha channel, at 8 and 16 bits. The slowest is about 34 seconds with an alpha channel and 22 without, under 40, so the limit stays. `MaxAVIFOutputPixels`, `avifEffort`, "Routes" in `README.md`, the `TODO.md` entry, the fix commit's message and the PR body now give those times. WebP and JPEG XL, measured the same way, stay within the 23 and 27 seconds already given, and their comments now say so for any quality. 2. Rebased onto the current `next`. The `TODO.md` entry appears once, at the top of Completed Steps. Judgement call: the rework is folded into the fix commit so that its message gives the new times. The test commit is unchanged. Partially verified: the times come from the `vips` command of the runtime image's libvips 8.16.1, run with the settings govips passes, not through pixad. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-16 00:47:41 +02:00
Author
Collaborator

Review failed: needs-rework.

  1. TODO.md, this PR's entry in Completed Steps: it gives 96 seconds as the time an 8192x8192 image of random pixels with an alpha channel took to save as AVIF, then says "Each time is the slowest at any quality." 96 seconds is the time at quality 85, and that is how "Routes" in README.md, the avifEffort comment and the PR body give it. At quality 90 to 99 the same encode takes about a third longer. So for AVIF the entry gives a slowest time that is not the slowest, and it disagrees with the other places. Acceptable: the entry says 96 is the time at quality 85, as the other places do, or gives the slowest time across the quality range for that size, and "Each time is the slowest at any quality" covers only the times it is true of.
  2. The branch: next has added entries at the top of Completed Steps in TODO.md since the last rebase, and the PR no longer merges with it; Gitea reports it as not mergeable. Acceptable: the branch rebased onto the current next, with this PR's entry kept once, at the top of Completed Steps.
  3. The PR body is about 280 words, over the limit of about 250. Acceptable: a body of about 250 words or fewer.

Rule suppressed: the change to TestImageProcessor_WebPAtDefaultEffort waits on sneak's answer at #278 (comment) and is not counted as a finding.
Judgement call: an auto request above the AVIF limit is refused instead of being served in another format; README.md says so, and it is not counted as a finding.
Partially verified: the encode times were measured with the vips command of the runtime image's libvips 8.16.1 on one thread, with the settings govips passes, not through pixad.

Model: opus-5-5

Review failed: needs-rework. 1. `TODO.md`, this PR's entry in Completed Steps: it gives 96 seconds as the time an 8192x8192 image of random pixels with an alpha channel took to save as AVIF, then says "Each time is the slowest at any quality." 96 seconds is the time at quality 85, and that is how "Routes" in `README.md`, the `avifEffort` comment and the PR body give it. At quality 90 to 99 the same encode takes about a third longer. So for AVIF the entry gives a slowest time that is not the slowest, and it disagrees with the other places. Acceptable: the entry says 96 is the time at quality 85, as the other places do, or gives the slowest time across the quality range for that size, and "Each time is the slowest at any quality" covers only the times it is true of. 2. The branch: `next` has added entries at the top of Completed Steps in `TODO.md` since the last rebase, and the PR no longer merges with it; Gitea reports it as not mergeable. Acceptable: the branch rebased onto the current `next`, with this PR's entry kept once, at the top of Completed Steps. 3. The PR body is about 280 words, over the limit of about 250. Acceptable: a body of about 250 words or fewer. Rule suppressed: the change to `TestImageProcessor_WebPAtDefaultEffort` waits on sneak's answer at https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-143931 and is not counted as a finding. Judgement call: an `auto` request above the AVIF limit is refused instead of being served in another format; `README.md` says so, and it is not counted as a finding. Partially verified: the encode times were measured with the `vips` command of the runtime image's libvips 8.16.1 on one thread, with the settings govips passes, not through pixad. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-16 02:44:21 +02:00
Author
Collaborator

Answered by #280 (comment): the change to TestImageProcessor_WebPAtDefaultEffort is allowed, as far as it moves the test to the fixed effort; this PR goes to one rework for #278 (comment).

Model: opus-5-5

Answered by https://git.eeqj.de/sneak/pixa/issues/280#issuecomment-151029: the change to `TestImageProcessor_WebPAtDefaultEffort` is allowed, as far as it moves the test to the fixed effort; this PR goes to one rework for https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-144377. Model: opus-5-5
clawbot force-pushed issue-272-encode-time-at-the-limits from 47a2c2360a to 94979140c1 2026-10-20 14:22:58 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 14:23:09 +02:00
Author
Collaborator

Rework for #278 (comment):

  1. TODO.md now gives 96 seconds as the AVIF time at quality 85, and says "at any quality" only of the 23, 27 and 34 seconds; every place that gives these numbers agrees.
  2. Rebased onto the current next, with the entry once, at the top of Completed Steps. In the conflict, next's rule that a side worked out for fit=contain, inside or outside is at least 1 moved into outputDimensions, which now works out those sides for resize.
  3. The PR body is about 250 words; its line on the WebP test change cites #280 (comment).

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-144377: 1. `TODO.md` now gives 96 seconds as the AVIF time at quality 85, and says "at any quality" only of the 23, 27 and 34 seconds; every place that gives these numbers agrees. 2. Rebased onto the current `next`, with the entry once, at the top of Completed Steps. In the conflict, `next`'s rule that a side worked out for `fit=contain`, `inside` or `outside` is at least 1 moved into `outputDimensions`, which now works out those sides for `resize`. 3. The PR body is about 250 words; its line on the WebP test change cites https://git.eeqj.de/sneak/pixa/issues/280#issuecomment-151029. Model: opus-5-5
Author
Collaborator

Review failed: needs-rebase.

  1. The branch: next has since added two entries at the top of Completed Steps in TODO.md, and Gitea reports the PR as not mergeable; merged as it stands, this PR's entry would sit third, under them. Acceptable: the branch rebased onto the current next, with this PR's entry once, at the top of Completed Steps.

Partially verified: the encode times were spot-checked with the vips command of the runtime image's libvips 8.16.1 on one thread, not through pixad, for the slowest cases with an alpha channel (WebP and JPEG XL at 8192x8192, AVIF at 4096x4096) at 8 and 16 bits and AVIF without one; the times before the change, the 8192x2048 and 2048x8192 shapes, and WebP and JPEG XL without an alpha channel were not measured.
Judgement call: an auto request above the AVIF limit is refused instead of being served in another format; README.md says so, and it is not counted as a finding.

Model: opus-5-5

Review failed: needs-rebase. 1. The branch: `next` has since added two entries at the top of Completed Steps in `TODO.md`, and Gitea reports the PR as not mergeable; merged as it stands, this PR's entry would sit third, under them. Acceptable: the branch rebased onto the current `next`, with this PR's entry once, at the top of Completed Steps. Partially verified: the encode times were spot-checked with the `vips` command of the runtime image's libvips 8.16.1 on one thread, not through pixad, for the slowest cases with an alpha channel (WebP and JPEG XL at 8192x8192, AVIF at 4096x4096) at 8 and 16 bits and AVIF without one; the times before the change, the 8192x2048 and 2048x8192 shapes, and WebP and JPEG XL without an alpha channel were not measured. Judgement call: an `auto` request above the AVIF limit is refused instead of being served in another format; `README.md` says so, and it is not counted as a finding. Model: opus-5-5
clawbot added needs-rebase and removed needs-review labels 2026-10-20 14:42:58 +02:00
clawbot force-pushed issue-272-encode-time-at-the-limits from 94979140c1 to 81183d6122 2026-10-20 14:46:20 +02:00 Compare
clawbot added needs-review and removed needs-rebase labels 2026-10-20 14:47:38 +02:00
Author
Collaborator

Rebased onto next, with this PR's entry at the top of Completed Steps in TODO.md. Model: opus-5-5

Rebased onto `next`, with this PR's entry at the top of Completed Steps in `TODO.md`. Model: opus-5-5
Author
Collaborator

Review failed: needs-rework.

  1. internal/imageprocessor/avif_too_large_internal_test.go, TestImageProcessor_RefusesAVIFAboveMaxPixels: every case asks for a size that resizes the image, so no test covers the refusal when the AVIF output keeps the source's size. That happens with avif and no size, and with orig of an AVIF source, which README.md names. A request for AVIF with no size, of an 8192x8192 source, is the plainest way to start the encode that #272 measured at about 96 to 110 seconds, and the test's doc comment says it covers "whatever size and fit give it". Acceptable: cases for a source of more than 16,777,216 pixels requested as avif with no size, and for orig of an AVIF source of more than 16,777,216 pixels, each refused with ErrAVIFOutputTooLarge.

Judgement call: an auto request above the AVIF limit is refused instead of being served in another format; README.md says so, and it is not counted as a finding.
Not verified: the encode times were not measured again, as the encoder settings and the AVIF limit are unchanged since the head reviewed at #278 (comment).

Model: opus-5-5

Review failed: needs-rework. 1. `internal/imageprocessor/avif_too_large_internal_test.go`, `TestImageProcessor_RefusesAVIFAboveMaxPixels`: every case asks for a size that resizes the image, so no test covers the refusal when the AVIF output keeps the source's size. That happens with `avif` and no size, and with `orig` of an AVIF source, which `README.md` names. A request for AVIF with no size, of an 8192x8192 source, is the plainest way to start the encode that https://git.eeqj.de/sneak/pixa/issues/272 measured at about 96 to 110 seconds, and the test's doc comment says it covers "whatever size and fit give it". Acceptable: cases for a source of more than 16,777,216 pixels requested as `avif` with no size, and for `orig` of an AVIF source of more than 16,777,216 pixels, each refused with `ErrAVIFOutputTooLarge`. Judgement call: an `auto` request above the AVIF limit is refused instead of being served in another format; `README.md` says so, and it is not counted as a finding. Not verified: the encode times were not measured again, as the encoder settings and the AVIF limit are unchanged since the head reviewed at https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-151150. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 14:56:56 +02:00
clawbot added 2 commits 2026-10-20 15:05:52 +02:00
The WebP test now expects effort 2 instead of libvips' default of 4, a
new test expects JPEG XL at effort 4 instead of 7, and new tests expect an
AVIF output of more than 4096x4096 pixels, at a requested size or at the
source's own, to be refused before anything is decoded and answered 400
on both image routes. They fail until the settings and the refusal are
in.

Model: opus-5-5
WebP is saved at effort 2 and JPEG XL at effort 4, and an AVIF output of
more than 16,777,216 pixels is refused with 400 before anything is
decoded, as AVIF is already at the lowest effort govips can set. With
one libvips thread, at any quality, 8192x8192 random pixels with an
alpha channel take up to about 23 seconds as WebP and 27 as JPEG XL, and
4096x4096, 8192x2048 or 2048x8192 up to about 34 as AVIF. Resizing with
fit=cover already decodes the input, so the output's dimensions are now
worked out before resizing, in outputDimensions. Reading the input moves
into readInput to keep Process within the linter's limits.

Model: opus-5-5
clawbot force-pushed issue-272-encode-time-at-the-limits from 81183d6122 to 8213f99f67 2026-10-20 15:05:52 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 15:06:47 +02:00
Author
Collaborator

Rework for #278 (comment): TestImageProcessor_RefusesAVIFAboveMaxPixels now has cases for avif with no size and for orig of an AVIF source, both refused, using the 4x4 testdata/red.avif with its header changed to 4097x4096, plus a JPEG case showing that input fails to decode. Model: opus-5-5

Rework for https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-151187: `TestImageProcessor_RefusesAVIFAboveMaxPixels` now has cases for `avif` with no size and for `orig` of an AVIF source, both refused, using the 4x4 `testdata/red.avif` with its header changed to 4097x4096, plus a JPEG case showing that input fails to decode. Model: opus-5-5
Author
Collaborator

Review passed: #278 meets #272 and is ready to merge into next.

Not verified: the encode times were not measured again, as the encoder settings and the AVIF limit are unchanged since the head reviewed at #278 (comment).
Judgement call: an auto request above the AVIF limit is refused instead of being served in another format; README.md says so, and it is not counted as a finding.

Model: opus-5-5

Review passed: https://git.eeqj.de/sneak/pixa/pulls/278 meets https://git.eeqj.de/sneak/pixa/issues/272 and is ready to merge into `next`. Not verified: the encode times were not measured again, as the encoder settings and the AVIF limit are unchanged since the head reviewed at https://git.eeqj.de/sneak/pixa/pulls/278#issuecomment-151150. Judgement call: an `auto` request above the AVIF limit is refused instead of being served in another format; `README.md` says so, and it is not counted as a finding. Model: opus-5-5
clawbot merged commit 989da78681 into next 2026-10-20 15:19:08 +02:00
clawbot deleted branch issue-272-encode-time-at-the-limits 2026-10-20 15:19:08 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#278