Order and sample gradient correctly
Zero-width gradient sections should be ‘ignored’. The visible endpoints should be precisely the first and last colors added at that point. https://html.spec.whatwg.org/multipage/canvas.html#interpolation
This commit is contained in:
parent
6d8a89b8c5
commit
033a150639
|
@ -15,7 +15,7 @@ use pathfinder_geometry::transform2d::Transform2F;
|
||||||
use pathfinder_geometry::vector::Vector2F;
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
use pathfinder_geometry::util as geometry_util;
|
use pathfinder_geometry::util as geometry_util;
|
||||||
use pathfinder_simd::default::F32x2;
|
use pathfinder_simd::default::F32x2;
|
||||||
use std::cmp::{Ordering, PartialOrd};
|
use std::cmp::Ordering;
|
||||||
use std::convert;
|
use std::convert;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -110,7 +110,7 @@ impl Gradient {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add(&mut self, stop: ColorStop) {
|
pub fn add(&mut self, stop: ColorStop) {
|
||||||
let index = self.stops.binary_search_by(|other| {
|
let index = self.stops.binary_search_by(|other| {
|
||||||
other.offset.partial_cmp(&stop.offset).unwrap()
|
if other.offset <= stop.offset { Ordering::Less } else { Ordering::Greater }
|
||||||
}).unwrap_or_else(convert::identity);
|
}).unwrap_or_else(convert::identity);
|
||||||
self.stops.insert(index, stop);
|
self.stops.insert(index, stop);
|
||||||
}
|
}
|
||||||
|
@ -138,8 +138,9 @@ impl Gradient {
|
||||||
|
|
||||||
t = geometry_util::clamp(t, 0.0, 1.0);
|
t = geometry_util::clamp(t, 0.0, 1.0);
|
||||||
let last_index = self.stops.len() - 1;
|
let last_index = self.stops.len() - 1;
|
||||||
|
|
||||||
let upper_index = self.stops.binary_search_by(|stop| {
|
let upper_index = self.stops.binary_search_by(|stop| {
|
||||||
stop.offset.partial_cmp(&t).unwrap_or(Ordering::Less)
|
if stop.offset < t || stop.offset == 0.0 { Ordering::Less } else { Ordering::Greater }
|
||||||
}).unwrap_or_else(convert::identity).min(last_index);
|
}).unwrap_or_else(convert::identity).min(last_index);
|
||||||
let lower_index = if upper_index > 0 { upper_index - 1 } else { upper_index };
|
let lower_index = if upper_index > 0 { upper_index - 1 } else { upper_index };
|
||||||
|
|
||||||
|
@ -205,3 +206,37 @@ impl RadialGradientLine for Vector2F {
|
||||||
LineSegment2F::new(self, self)
|
LineSegment2F::new(self, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::gradient::Gradient;
|
||||||
|
use pathfinder_color::ColorU;
|
||||||
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stable_order() {
|
||||||
|
let mut grad = Gradient::linear_from_points(Vector2F::default(), Vector2F::default());
|
||||||
|
for i in 0..110 {
|
||||||
|
grad.add_color_stop(ColorU::new(i, 0, 0, 1), (i % 11) as f32 / 10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that it sorted stably
|
||||||
|
assert!(grad.stops.windows(2).all(|w| {
|
||||||
|
w[0].offset < w[1].offset || w[0].color.r < w[1].color.r
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn never_sample_zero_width() {
|
||||||
|
let mut grad = Gradient::linear_from_points(Vector2F::default(), Vector2F::default());
|
||||||
|
for i in 0..110 {
|
||||||
|
let zero_width = (i == 0) || (11 <= i && i < 99) || (i == 109);
|
||||||
|
grad.add_color_stop(ColorU::new(if zero_width { 255 } else { 0 }, 0, 0, 1), (i % 11) as f32 / 10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..11 {
|
||||||
|
let sample = grad.sample(i as f32 / 10.0);
|
||||||
|
assert!(sample.r == 0, "{} {}", i, sample.r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue