Skip to content

Fix excessive sampling of linear gradients in PDF and SVG#7818

Merged
laurmaedje merged 4 commits intotypst:mainfrom
eltos:eltos-fix-gradient-sampling
Feb 13, 2026
Merged

Fix excessive sampling of linear gradients in PDF and SVG#7818
laurmaedje merged 4 commits intotypst:mainfrom
eltos:eltos-fix-gradient-sampling

Conversation

@eltos
Copy link
Contributor

@eltos eltos commented Feb 6, 2026

Description

Commit a82dce9
Fixes duplicating stops at equal offset during interpolation, which caused sharp gradients in PDF to become blurry.
Also fixes duplicating stops in SVG where both stops of the (overlapping) window strides were added.
This results in visual changes (fixes) to the tests containing sharp gradients (see examples below).

Commit ba121d9
Instead of always inserting a fixed number of in-between-stops (30 for PDF, 256/len for SVG), only insert actually required stops.
How many stops are required is determined by comparing the linearly rgb-interpolated color with the actual color at the midpoints, iteratively subdividing the distance between stops until the error falls below a threshold.
The threshold of 0.001 used results in no visible change.
I also tested with 0.01 where there are very small almost unnoticable changes (see examples below).
Since the step 0.001 to 0.01 reduces the size only little, I decided to use 0.001 in the end, but this can be adjusted if desired.

Size statistics for cargo testit gradient

Before this PR Duplication fix With th=0.001 (With th=0.01)
Stop count bloat factor in PDF export 28.7 16.2 15.0 14.6
total size of PDF files with gradient tests 67.9 MB 63.2 MB 2.9 MB 2.7 MB
Stop count bloat factor in SVG export 30.2 29.8 4.4 1.9
total size of SVG files with gradient tests 13.1 MB 11.3 MB 10.5 MB 10.4 MB

Stop count bloat factor denotes the number of stops after the interpolation, compared to the number of stops of the original gradient (mean of factors for all tests)

Changes (examples)

Changes due to sharp gradient fix:

gradient-linear-stroke-relative-parent first_commit curve-stroke-gradient-sharp first_commit issue-6680-gradient-linear-with-aspect-correction first_commit

Changes in case of th=0.01:

grafik grafik grafik

@eltos eltos mentioned this pull request Feb 7, 2026
1 task
@LaurenzV
Copy link
Collaborator

LaurenzV commented Feb 7, 2026

I've mentioned this somewhere else, but in case it hasn't been tried, I would strongly suggest trying to use the color crate and the gradient method to create the optimal number and location of stops to approximate the color in SRGB. I don't know the theory behind it, but I think they use some more advanced approach to determine the best approximation for the given color space. This won't work for CMYK unfortunately, so we would need fallback behavior there, but I believe all other color spaces are supported.

@eltos
Copy link
Contributor Author

eltos commented Feb 7, 2026

Thank you, I looked at how they do it and basically followed the same algorithm: bisecting the gradient until the Euclidean difference between the rgb-linearly interpolated midpoint color and the midpoint actual color between two stops falls below a threshold. I decided against using the color create to avoid adding it as dependency, to avoid the copying and type conversion necessary to call that method, to avoid the non-CMYK restriction and because in the end it's a rather simple algorithm.

@LaurenzV
Copy link
Collaborator

LaurenzV commented Feb 7, 2026

Fair enough!

@laurmaedje laurmaedje added waiting-on-review This PR is waiting to be reviewed. pdf Related to PDF export or PDF embedding. fix A bug fix. labels Feb 8, 2026
@laurmaedje
Copy link
Member

@eltos Is this ready for review? Since this and your other PR are still marked as draft.

@eltos
Copy link
Contributor Author

eltos commented Feb 9, 2026

Not yet, there is a similar stop duplication behaviour in SVG export which I plan to cover, too. Please bear with me, I'll notify you once I come to it and the PR is ready.

@laurmaedje laurmaedje removed the waiting-on-review This PR is waiting to be reviewed. label Feb 9, 2026
@laurmaedje
Copy link
Member

laurmaedje commented Feb 9, 2026

Sure, no rush, it's just that sometimes people open a draft PR when they're actually waiting for a review, so I wanted to double check.

@eltos eltos force-pushed the eltos-fix-gradient-sampling branch 2 times, most recently from d42c217 to ba121d9 Compare February 9, 2026 17:47
@eltos eltos marked this pull request as ready for review February 9, 2026 17:57
@eltos
Copy link
Contributor Author

eltos commented Feb 9, 2026

@laurmaedje PR ready for review. SVG handling added and threshold lowered to 0.001, see details in updated PR description.

@eltos eltos changed the title Fix excessive sampling of linear gradients in PDF Fix excessive sampling of linear gradients in PDF and SVG Feb 9, 2026
@laurmaedje laurmaedje added the waiting-on-review This PR is waiting to be reviewed. label Feb 10, 2026
@saecki saecki self-assigned this Feb 10, 2026
Copy link
Member

@saecki saecki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! Just one little nitpick. This also reduces the time for the test suite to run on my machine from ~4.3s to ~3.1s.

@eltos eltos force-pushed the eltos-fix-gradient-sampling branch from 3c3b8fb to 76d966c Compare February 10, 2026 19:05
@laurmaedje laurmaedje removed the waiting-on-review This PR is waiting to be reviewed. label Feb 13, 2026
@laurmaedje laurmaedje added this pull request to the merge queue Feb 13, 2026
Merged via the queue into typst:main with commit 4efe3a7 Feb 13, 2026
8 checks passed
@laurmaedje
Copy link
Member

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix A bug fix. pdf Related to PDF export or PDF embedding.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Excessive sampling of linear gradients in PDF gradient.linear with sharp will alias depending on space used

4 participants