Fix up merged PRs: #588's polygon clip and nested metafiles with clip regions

Move #588's clip.rs changes into cliprect.rs, carry main's per-item clip
region through the polygon clipper, and build nested-metafile items with the
region field main added (the nested picture's own regions are not carried
over; each item takes the canvas's current clip).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas
2026-10-14 13:56:56 -04:00
co-authored by Claude Opus 5.5
parent 4d7406be10
commit 389c4dd5d0
2 changed files with 24 additions and 17 deletions
+10 -10
View File
@@ -143,10 +143,10 @@ fn bbox_inside(b: [f64; 4], clip: &[P]) -> bool {
pub(crate) fn clip_raw(raw: Raw, clip: &[P]) -> Vec<Raw> {
let Some(cb) = pts_bbox(clip).filter(|_| clip.len() >= 3) else { return Vec::new() };
match raw {
Raw::Path { ops, fill, stroke, even_odd } => {
Raw::Path { ops, fill, stroke, even_odd, region } => {
let Some(b) = ops_bbox(&ops) else { return Vec::new() };
if bbox_inside(b, clip) {
return vec![Raw::Path { ops, fill, stroke, even_odd }];
return vec![Raw::Path { ops, fill, stroke, even_odd, region }];
}
if disjoint(b, cb) {
return Vec::new();
@@ -156,34 +156,34 @@ pub(crate) fn clip_raw(raw: Raw, clip: &[P]) -> Vec<Raw> {
if fill.is_some() {
let f = clip_fill(&subs, clip);
if !f.is_empty() {
out.push(Raw::Path { ops: f, fill, stroke: None, even_odd });
out.push(Raw::Path { ops: f, fill, stroke: None, even_odd, region: region.clone() });
}
}
if stroke.is_some() {
let s = clip_stroke(&subs, clip);
if !s.is_empty() {
out.push(Raw::Path { ops: s, fill: None, stroke, even_odd });
out.push(Raw::Path { ops: s, fill: None, stroke, even_odd, region });
}
}
out
}
Raw::Pattern { ops, even_odd, tile } => {
Raw::Pattern { ops, even_odd, tile, region } => {
let Some(b) = ops_bbox(&ops) else { return Vec::new() };
if bbox_inside(b, clip) {
return vec![Raw::Pattern { ops, even_odd, tile }];
return vec![Raw::Pattern { ops, even_odd, tile, region }];
}
if disjoint(b, cb) {
return Vec::new();
}
let f = clip_fill(&flatten(&ops), clip);
if f.is_empty() { Vec::new() } else { vec![Raw::Pattern { ops: f, even_odd, tile }] }
if f.is_empty() { Vec::new() } else { vec![Raw::Pattern { ops: f, even_odd, tile, region }] }
}
Raw::Bitmap { rect, img } => crop(rect, img, clip).map(|(rect, img)| vec![Raw::Bitmap { rect, img }]).unwrap_or_default(),
Raw::Text(mut t) => {
Raw::Bitmap { rect, img, region } => crop(rect, img, clip).map(|(rect, img)| vec![Raw::Bitmap { rect, img, region }]).unwrap_or_default(),
Raw::Text { text: mut t, region } => {
// Text clips to a rectangle: the one around the polygon.
let cur = t.clip.map_or(cb, |[a, b, d, e]| intersect(cb, [f64::from(a), f64::from(b), f64::from(d), f64::from(e)]));
t.clip = Some(cur.map(|v| v as f32));
vec![Raw::Text(t)]
vec![Raw::Text { text: t, region }]
}
}
}
+14 -7
View File
@@ -737,15 +737,21 @@ impl Painter {
})
.collect()
};
// The nested picture's own clip regions are not carried over: each item takes the
// canvas's current clip.
for item in &pic.items {
if cv.full() {
break;
}
let raw = match item {
Item::Path { segs, fill, stroke, even_odd } => {
Raw::Path { ops: ops(segs), fill: *fill, stroke: stroke.map(|(c, w)| (c, f64::from(w) * scale)), even_odd: *even_odd }
}
Item::Pattern { segs, even_odd, tile } => Raw::Pattern {
Item::Path { segs, fill, stroke, even_odd, .. } => Raw::Path {
ops: ops(segs),
fill: *fill,
stroke: stroke.map(|(c, w)| (c, f64::from(w) * scale)),
even_odd: *even_odd,
region: None,
},
Item::Pattern { segs, even_odd, tile, .. } => Raw::Pattern {
ops: ops(segs),
even_odd: *even_odd,
tile: Arc::new(RawTile {
@@ -754,12 +760,13 @@ impl Painter {
px: tile.rgba.clone(),
cell: f64::from(tile.cell[0]) * scale,
}),
region: None,
},
Item::Bitmap { rect, width, height, rgba } => {
Item::Bitmap { rect, width, height, rgba, .. } => {
let (a, b) = (pt(rect[0], rect[1]), pt(rect[0] + rect[2], rect[1] + rect[3]));
let mut img = Rgba { w: *width as usize, h: *height as usize, bottom_up: false, px: rgba.clone() };
crate::dib::flip(&mut img, a.0 > b.0, a.1 > b.1);
Raw::Bitmap { rect: [a.0.min(b.0), a.1.min(b.1), (b.0 - a.0).abs(), (b.1 - a.1).abs()], img }
Raw::Bitmap { rect: [a.0.min(b.0), a.1.min(b.1), (b.0 - a.0).abs(), (b.1 - a.1).abs()], img, region: None }
}
Item::Text(t) => {
let mut t = t.clone();
@@ -768,7 +775,7 @@ impl Painter {
let (p, q) = (pt(x0, y0), pt(x1, y1));
t.clip = Some([p.0.min(q.0) as f32, p.1.min(q.1) as f32, p.0.max(q.0) as f32, p.1.max(q.1) as f32]);
}
Raw::Text(t)
Raw::Text { text: t, region: None }
}
};
cv.push_device(raw);