Pictures: a phone photo as avatar or logo holds a web worker for seconds re-encoding full-size PNGs #482
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#482
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, readingcore/pictures.pyand timingas_storedon camera-sized JPEGs.Related to #265.
Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now ataccounts/forms.py:397(the profile form's call toavatars.process);core/pictures.py,accounts/avatars.py:78,jobs/logos.py:97and theDockerfilehave not changed.as_storedkeeps the image at full resolution, converts it to RGBA and callsencode_within, which writes a complete PNG withoptimize=True, and when that is over the 1 MB budget shrinks the picture by only 20% and encodes it again, until it fits. A photograph never compresses to 1 MB as PNG at camera resolution, so an ordinary phone photo goes through eight or nine full optimising encodes before it is stored at around 600 pixels. The module's comment says the reduction "effectively never runs"; that holds for flat artwork only.core/pictures.py:88-112,encode_within(REDUCTION = 0.8at:57);core/pictures.py:115-131,as_stored; called fromaccounts/avatars.py:78(profile picture upload, inside the request viaaccounts/forms.py:396) andjobs/logos.py:97(logo upload, and Find logo inCompanyLogoActionView).square=Truetook 10.3 s and 8 PNG encodes and stored 628×628;square=Falsetook 13.8 s and 9 encodes. A 1600×1200 photo took 1.4–1.9 s and 4 encodes. The other reviewer's figures for real-sized JPEGs were of the same order (6–17 s).docker/Dockerfile:354-362). A person setting a picture, retrying because it seems stuck, or two people doing it at once, leaves the instance with one worker or none for everyone else for that time. The result is no better for the wait: the stored picture is the size a single resize would have produced.Image.thumbnailto, say, 2048, withImage.draftfor JPEG so the decode itself is cheaper); estimate the next scale from the first attempt (aboutsqrt(budget / written)with a margin) instead of 20% steps; encode trial passes withoutoptimizeand only the final one with it. Correct the comment onDEFAULT_BUDGET.Done when
tests/test_pictures.pyfeedsas_storeda 4000×3000 photographic JPEG and asserts that at most two PNG encodes happen (counted by patchingImage.Image.save) and that the result fits the budget; it fails today.Landed on main with a green Forgejo CI run. Its entry in the CHANGELOG's Unreleased section ends in (#482).