summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author+merlan #flirora <flirora@flirora.xyz>2025-06-27 05:26:15 -0400
committerGitHub <noreply@github.com>2025-06-27 09:26:15 +0000
commit584dd5fec69ad80feedf4018e02b427f2c812716 (patch)
treede385a34447fee22e90b163d9e521ff2953c48ef
parentb9f3a95e03bd792bca4926959eab5f9f6edd1a9d (diff)
Fix panic when sampling across two coincident gradient stops (#6166)
-rw-r--r--crates/typst-library/src/visualize/gradient.rs21
-rw-r--r--tests/ref/issue-6162-coincident-gradient-stops-export-png.pngbin0 -> 522 bytes
-rw-r--r--tests/suite/visualize/gradient.typ26
3 files changed, 33 insertions, 14 deletions
diff --git a/crates/typst-library/src/visualize/gradient.rs b/crates/typst-library/src/visualize/gradient.rs
index 5d7859a3..4917da68 100644
--- a/crates/typst-library/src/visualize/gradient.rs
+++ b/crates/typst-library/src/visualize/gradient.rs
@@ -1285,24 +1285,17 @@ fn process_stops(stops: &[Spanned<GradientStop>]) -> SourceResult<Vec<(Color, Ra
/// Sample the stops at a given position.
fn sample_stops(stops: &[(Color, Ratio)], mixing_space: ColorSpace, t: f64) -> Color {
let t = t.clamp(0.0, 1.0);
- let mut low = 0;
- let mut high = stops.len();
+ let mut j = stops.partition_point(|(_, ratio)| ratio.get() < t);
- while low < high {
- let mid = (low + high) / 2;
- if stops[mid].1.get() < t {
- low = mid + 1;
- } else {
- high = mid;
+ if j == 0 {
+ while stops.get(j + 1).is_some_and(|(_, r)| r.is_zero()) {
+ j += 1;
}
+ return stops[j].0;
}
- if low == 0 {
- low = 1;
- }
-
- let (col_0, pos_0) = stops[low - 1];
- let (col_1, pos_1) = stops[low];
+ let (col_0, pos_0) = stops[j - 1];
+ let (col_1, pos_1) = stops[j];
let t = (t - pos_0.get()) / (pos_1.get() - pos_0.get());
Color::mix_iter(
diff --git a/tests/ref/issue-6162-coincident-gradient-stops-export-png.png b/tests/ref/issue-6162-coincident-gradient-stops-export-png.png
new file mode 100644
index 00000000..d269342c
--- /dev/null
+++ b/tests/ref/issue-6162-coincident-gradient-stops-export-png.png
Binary files differ
diff --git a/tests/suite/visualize/gradient.typ b/tests/suite/visualize/gradient.typ
index 811b8b60..8446ca03 100644
--- a/tests/suite/visualize/gradient.typ
+++ b/tests/suite/visualize/gradient.typ
@@ -666,3 +666,29 @@ $ A = mat(
#let _ = gradient.linear(..my-gradient.stops())
#let my-gradient2 = gradient.linear(red, blue).repeat(5, mirror: true)
#let _ = gradient.linear(..my-gradient2.stops())
+
+--- issue-6162-coincident-gradient-stops-export-png ---
+// Ensure that multiple gradient stops with the same position
+// don't cause a panic.
+#rect(
+ fill: gradient.linear(
+ (red, 0%),
+ (green, 0%),
+ (blue, 100%),
+ )
+)
+#rect(
+ fill: gradient.linear(
+ (red, 0%),
+ (green, 100%),
+ (blue, 100%),
+ )
+)
+#rect(
+ fill: gradient.linear(
+ (white, 0%),
+ (red, 50%),
+ (green, 50%),
+ (blue, 100%),
+ )
+)