EPS import: a stitched shading keeps its hard colour changes (#1653)
A stitching function's bound became one stop, evaluated at the bound: the right piece's colour. Where the pieces don't meet, the left piece's end colour was lost. The colour just before the bound is now a stop at the same offset too, ahead of the one after it, when the two differ; continuous stitches keep one stop per bound as before. Fixes #1653
This commit is contained in:
parent
85335e9e14
commit
3412d3a073
|
|
@ -39,8 +39,9 @@ const MAX_PATH: usize = 1 << 20;
|
|||
const MAX_DRAWN: usize = 1 << 24;
|
||||
/// Samples taken of a shading function that isn't a plain interpolation.
|
||||
const SHADING_SAMPLES: usize = 32;
|
||||
/// Most bounds of a stitched linear function that become a gradient's stops: past it, the
|
||||
/// function is sampled like any other (each stop evaluates the whole function).
|
||||
/// Most bounds of a stitched linear function that become a gradient's stops (two where the pieces
|
||||
/// don't meet): past it, the function is sampled like any other (each stop evaluates the whole
|
||||
/// function).
|
||||
const MAX_STITCH_STOPS: usize = 1024;
|
||||
/// Most functions one evaluation of a shading's function visits: the function itself, each entry
|
||||
/// of an array of functions and, in each stitching function, the piece that applies to the input.
|
||||
|
|
@ -1098,7 +1099,9 @@ impl Interp<'_> {
|
|||
// Where the colour changes: the ends, and the bounds of stitched linear functions (unless
|
||||
// there are too many).
|
||||
let mut visits = 0;
|
||||
let ts: Vec<f64> = match linear_points(&f, 0, &mut visits).filter(|p| p.len() <= MAX_STITCH_STOPS) {
|
||||
let points = linear_points(&f, 0, &mut visits).filter(|p| p.len() <= MAX_STITCH_STOPS);
|
||||
let stitched = points.is_some();
|
||||
let ts: Vec<f64> = match points {
|
||||
Some(mut pts) => {
|
||||
pts.retain(|t| (d0.min(d1)..=d0.max(d1)).contains(t));
|
||||
pts.iter().map(|t| if d1 != d0 { (t - d0) / (d1 - d0) } else { 0.0 }).chain([0.0, 1.0]).collect()
|
||||
|
|
@ -1112,15 +1115,25 @@ impl Interp<'_> {
|
|||
let c = |i: usize| coords.get(i).copied().unwrap_or(0.0);
|
||||
let (r0, r1) = (c(2).max(0.0), c(5).max(1e-6));
|
||||
let mut stops = Vec::with_capacity(ts.len());
|
||||
let stop = |paint: Paint, offset: f64| match paint {
|
||||
Paint::Solid { color, swatch, tint } => GradientStop { swatch, tint, ..GradientStop::new(offset as f32, color) },
|
||||
_ => GradientStop::new(offset as f32, Color::BLACK),
|
||||
};
|
||||
for t in ts {
|
||||
let comps = self.eval(&f, &[d0 + (d1 - d0) * t], 0)?;
|
||||
let paint = self.paint_of(&space, &comps)?;
|
||||
let offset = if radial { (r0 + (r1 - r0) * t) / r1 } else { t };
|
||||
let stop = match paint {
|
||||
Paint::Solid { color, swatch, tint } => GradientStop { swatch, tint, ..GradientStop::new(offset as f32, color) },
|
||||
_ => GradientStop::new(offset as f32, Color::BLACK),
|
||||
};
|
||||
stops.push(stop);
|
||||
// At a bound of a stitched function the pieces need not meet (a hard colour change):
|
||||
// the colour just before the bound is a stop at the same offset, ahead of the one
|
||||
// after it.
|
||||
if stitched && t > 0.0 && t < 1.0 {
|
||||
let comps = self.eval(&f, &[d0 + (d1 - d0) * (t - 1e-9)], 0)?;
|
||||
let before = stop(self.paint_of(&space, &comps)?, offset);
|
||||
if before.color != stop(paint.clone(), offset).color {
|
||||
stops.push(before);
|
||||
}
|
||||
}
|
||||
stops.push(stop(paint, offset));
|
||||
}
|
||||
let (gk, geom) = if radial {
|
||||
let centre = Point::new(c(3), c(4));
|
||||
|
|
|
|||
|
|
@ -292,6 +292,19 @@ fn shadings_become_gradients() {
|
|||
assert_eq!(g.gradient.kind, GradientKind::Radial);
|
||||
let offsets: Vec<f32> = g.gradient.stops.iter().map(|s| s.offset).collect();
|
||||
assert_eq!(offsets, vec![0.0, 0.25, 1.0]);
|
||||
// Pieces that don't meet at a bound make a hard stop: the colour before the bound and the
|
||||
// one after it, at the same offset (the PDF reader does the same).
|
||||
let hard = "<< /ShadingType 2 /ColorSpace /DeviceRGB /Coords [0 0 200 0] /Function << /FunctionType 3 /Domain [0 1] /Bounds [0.5] /Encode [0 1 0 1] /Functions [<< /FunctionType 2 /Domain [0 1] /C0 [1 0 0] /C1 [0 0 1] /N 1 >> << /FunctionType 2 /Domain [0 1] /C0 [1 0 0] /C1 [0 0 1] /N 1 >>] >> >>";
|
||||
let r = read(&format!("0 0 200 50 rectclip {hard} shfill"));
|
||||
let o = objects(&r.document);
|
||||
let Some(Paint::Gradient(g)) = fill(&o[0]) else { panic!("{:?}", o[0]) };
|
||||
let stops: Vec<(f32, Color)> = g.gradient.stops.iter().map(|s| (s.offset, s.color)).collect();
|
||||
let (red, blue) = (Color::rgb(1.0, 0.0, 0.0), Color::rgb(0.0, 0.0, 1.0));
|
||||
assert_eq!(stops.len(), 4, "{stops:?}");
|
||||
assert_eq!((stops[0], stops[3]), ((0.0, red), (1.0, blue)), "{stops:?}");
|
||||
assert_eq!((stops[1].0, stops[2]), (0.5, (0.5, red)), "{stops:?}");
|
||||
let near = |a: [f32; 3], b: [f32; 3]| a.iter().zip(b).all(|(x, y)| (x - y).abs() < 1e-6);
|
||||
assert!(near(stops[1].1.to_rgb(), blue.to_rgb()), "{stops:?}");
|
||||
}
|
||||
|
||||
/// Tiling patterns and spot inks are found again through a hash of what tells them apart, so a
|
||||
|
|
|
|||
|
|
@ -920,10 +920,12 @@ fn gradients_stitched_from_linear_pieces_keep_or_sample_their_stops() {
|
|||
/Functions [ {pieces} {{ F }} repeat ] /Bounds [ 1 1 {bounds} {{ {pieces} div }} for ] >> >> shfill"
|
||||
)
|
||||
};
|
||||
// A few pieces: the bounds are kept exactly as stops (three bounds -> five stops with the ends).
|
||||
assert_eq!(stitch_stops(&stitched(3)), vec![0.0, 0.25, 0.5, 0.75, 1.0]);
|
||||
// At the cap (1024 bounds, 1025 pieces) the 1024 bounds and the two ends are all kept.
|
||||
assert_eq!(stitch_stops(&stitched(1024)).len(), 1026);
|
||||
// A few pieces: the bounds are kept exactly as stops. Each piece runs from 0 to 1, so at every
|
||||
// bound the colour jumps: two stops there (three bounds -> eight stops with the ends).
|
||||
assert_eq!(stitch_stops(&stitched(3)), vec![0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0]);
|
||||
// At the cap (1024 bounds, 1025 pieces) the 1024 bounds (two stops each) and the two ends are
|
||||
// all kept.
|
||||
assert_eq!(stitch_stops(&stitched(1024)).len(), 2 * 1024 + 2);
|
||||
// One past it (1025 bounds, 1026 pieces) the function is sampled instead.
|
||||
assert_eq!(stitch_stops(&stitched(1025)).len(), 33);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue